Складне гніздування партизанів та шаблонів


503

Моє запитання стосується того, як займатися складним вкладом шаблонів (їх також називають частками ) у програмі AngularJS.

Найкращий спосіб описати мою ситуацію - це створене нами зображення:

Діаграма сторінок AngularJS

Як бачите, це може бути досить складною програмою з великою кількістю вкладених моделей.

Додаток є односторінковим, тому він завантажує index.html, який містить елемент DIV у DOM з ng-viewатрибутом.

Для кола 1 ви бачите, що існує Первинна навігація, яка завантажує відповідні шаблони в ng-view. Я роблю це, переходячи $routeParamsдо головного модуля програми. Ось приклад того, що в моєму додатку:

angular.module('myApp', []).
    config(['$routeProvider', function($routeProvider) {
        $routeProvider.                     
            when("/job/:jobId/zones/:zoneId", { controller: JobDetailController, templateUrl: 'assets/job_list_app/templates/zone_edit.html' }).
            when("/job/:jobId/initial_inspection", { controller: JobDetailController, templateUrl: 'assets/job_list_app/templates/initial_inspection.html' }).
            when("/job/:jobId/zones/:zoneId/rooms/:roomId", { controller: JobDetailController, templateUrl: 'assets/job_list_app/templates/room_edit.html' })       

    }]);

У другому колі шаблон, завантажений у програму, ng-viewмає додаткову навігацію . Тоді цей підроздріб повинен завантажувати шаблони в область під ним, але оскільки ng-view вже використовується, я не знаю, як це зробити.

Я знаю, що я можу включити додаткові шаблони до 1-го шаблону, але всі ці шаблони будуть досить складними. Я хотів би зберегти всі шаблони окремо, щоб полегшити оновлення програми та не мати залежності від батьківського шаблону, який потрібно завантажувати, щоб отримати доступ до своїх дітей.

У колі 3 ви бачите, що речі стають ще складнішими. Існує ймовірність того, що шаблони під навігації матимуть другу підвигун, яку потрібно буде завантажувати власними шаблонами також у область у колі 4

Як можна структурувати додаток AngularJS для вирішення таких складних вкладень шаблонів, зберігаючи їх окремо один від одного?


3
Якщо ви все ще слідуєте за цією темою, я додав посилання на новий проект AngularUI для вирішення цього питання і третю демонстраційну версію, яка працює на підпрограмах, не потребуючи директиви для моєї відповіді.
— ProLoser

2
як щодо цього? bennadel.com/blog/…
— ericbae

82
найкрасивіше запитання, яке я бачив давно :)
— Марк Надіг

2
домовились! Любіть використовувати макет
— Брайан Гонг

4
Було б здорово додати посилання на альтернативи безпосередньо у питанні тут. github.com/angular-ui/ui-router github.com/artch/angular-route-segment github.com/dotJEM/angular-routing
— Jens

Відповіді:


171

Ну, оскільки на даний момент ви можете мати лише одну директиву ngView ... я використовую вкладені елементи управління. Це дозволяє налаштувати шаблони та успадкувати (або виділити) сфери застосування серед них. Поза цим я використовую ng-перемикач або навіть просто ng-show, щоб вибрати, які елементи управління відображатиму, виходячи з того, що надходить з $ routeParams.

EDIT Ось декілька прикладів псевдо-коду, щоб дати вам уявлення про те, про що я говорю. З вкладеною додатковою навігацією.

Ось основна сторінка програми

<!-- primary nav -->
<a href="#/page/1">Page 1</a>
<a href="#/page/2">Page 2</a>
<a href="#/page/3">Page 3</a>

<!-- display the view -->
<div ng-view>
</div>

Директива для підводного навігації

app.directive('mySubNav', function(){
    return {
        restrict: 'E',
        scope: {
           current: '=current'
        },
        templateUrl: 'mySubNav.html',
        controller: function($scope) {
        }
    };
});

шаблон для додаткової навігації

<a href="#/page/1/sub/1">Sub Item 1</a>
<a href="#/page/1/sub/2">Sub Item 2</a>
<a href="#/page/1/sub/3">Sub Item 3</a>

шаблон для головної сторінки (з первинного наві)

<my-sub-nav current="sub"></my-sub-nav>

<ng-switch on="sub">
  <div ng-switch-when="1">
      <my-sub-area1></my-sub-area>
  </div>
  <div ng-switch-when="2">
      <my-sub-area2></my-sub-area>
  </div>
  <div ng-switch-when="3">
      <my-sub-area3></my-sub-area>
  </div>
</ng-switch>

Контролер основної сторінки. (від первинного нави)

app.controller('page1Ctrl', function($scope, $routeParams) {
     $scope.sub = $routeParams.sub;
});

Директива для підзони

app.directive('mySubArea1', function(){
    return {
        restrict: 'E',
        templateUrl: 'mySubArea1.html',
        controller: function($scope) {
            //controller for your sub area.
        }
    };
});

9
Мені більше подобається рішення ProLoser, ми робимо щось подібне у нашому додатку, і воно спрацювало чудово. Проблема з рішенням blesh полягає в тому, що код контролера потрапляє в директиви. Зазвичай контролер, який ви вказали в директиві, - це той, який тісно співпрацює з директивою для ex: ngModelCtrl, який тісно співпрацює з вхідними та іншими директивами. У вашому випадку введення коду контролера в директиву буде запахом коду, це насправді незалежний контролер.
— ChrisOdney

@blesh, приємно! Здається, це дуже добре пояснено, але, як хороший програміст JS та початківець в AngularJS, я можу це добре зрозуміти ... Мені та громаді дуже сподобається посилання JSFiddle до робочої вибірки, використовуючи такий підхід. Diggin це навколо було б легко зрозуміти! :) Дякую
— Роджер Баррето

8
Я думаю, що це рішення має стати першою відповіддю на будь-які питання типу "вкладений вигляд не спрацював". Просто тому, що це набагато ближче до кутової ідеології, а не використовувати ui-роутер і т. Д. Дякую.
— Сергій Панфілов

Отже, відповідь - директиви?
— Марк М.

щоб виділити пункти під-навігації, якими ви можете скористатися цим: coder1.com/articles/angularjs-managing-active-nav-elements це хороший спосіб зробити це?
— втекла котом

198

ОНОВЛЕННЯ: Перегляньте новий проект AngularUI для вирішення цієї проблеми


Для підрозділів це так просто, як використання рядків у ng-include:

<ul id="subNav">
  <li><a ng-click="subPage='section1/subpage1.htm'">Sub Page 1</a></li>
  <li><a ng-click="subPage='section1/subpage2.htm'">Sub Page 2</a></li>
  <li><a ng-click="subPage='section1/subpage3.htm'">Sub Page 3</a></li>
</ul>
<ng-include src="subPage"></ng-include>

Або ви можете створити об’єкт, якщо у вас є посилання на підсторінки в усьому місці:

$scope.pages = { page1: 'section1/subpage1.htm', ... };
<ul id="subNav">
  <li><a ng-click="subPage='page1'">Sub Page 1</a></li>
  <li><a ng-click="subPage='page2'">Sub Page 2</a></li>
  <li><a ng-click="subPage='page3'">Sub Page 3</a></li>
</ul>
<ng-include src="pages[subPage]"></ng-include>

Або ви навіть можете використовувати $routeParams

$routeProvider.when('/home', ...);
$routeProvider.when('/home/:tab', ...);
$scope.params = $routeParams;
<ul id="subNav">
  <li><a href="#/home/tab1">Sub Page 1</a></li>
  <li><a href="#/home/tab2">Sub Page 2</a></li>
  <li><a href="#/home/tab3">Sub Page 3</a></li>
</ul>
<ng-include src=" '/home/' + tab + '.html' "></ng-include>

Ви також можете поставити ng-контролер на самий верхній рівень кожної частки


8
Мені більше подобається ваше рішення. Я новачок у Angular, і це здається набагато зрозумілішим від того, як я бачу Інтернет до сьогодні. хто знає, може бути, блеш використовує більше кутових рамок, щоб зробити це, але здається, що ви прибили його меншою кількістю рядків коду більш розумним чином. Дякую!
— Гліб

2
@ProLooser можливо ви мали на увазі це посилання github.com/angular-ui/ui-router ... той, який ви вставили, зламаний
— simonC

4
У мене те саме питання дизайну, що і в ОП. Хтось приміряв механізм стану AngularUI? Я дуже неохоче користуюся ще однією сторонній бібліотекою, і я вважаю за краще дотримуватися AngularJS "способу робити речі". Але з іншого боку, система маршрутизації, здається, є п'ятою Ахілла ... @PhillipKregg, що ти в кінцевому підсумку використав для вирішення цього сценарію?
— Сем

4
Ви можете вказати <div ng-include="'/home/' + tab + '.html'" ng-controller="SubCtrl"></div>для того, щоб використовувати окремий контролер / область для під-шаблону. Або просто вкажіть ngControllerдирективу в будь-якому місці під-шаблонів, щоб використовувати різні контролери для кожної часткової.
— colllin

2
@DerekAdair проект досить стабільний (незважаючи на номер версії) і використовується у виробництві в декількох місцях. Це запобігає непотрібному перезавантаженню контролера і є набагато кращою альтернативою запропонованому нами рішення.
— ProLoser

26

Ви можете оформити цю бібліотеку з тією ж метою:

http://angular-route-segment.com

Це схоже на те, що ви шукаєте, і використовувати його набагато простіше, ніж ui-роутер. З демонстраційного сайту :

JS:

$routeSegmentProvider.

when('/section1',          's1.home').
when('/section1/:id',      's1.itemInfo.overview').
when('/section2',          's2').

segment('s1', {
    templateUrl: 'templates/section1.html',
    controller: MainCtrl}).
within().
    segment('home', {
        templateUrl: 'templates/section1/home.html'}).
    segment('itemInfo', {
        templateUrl: 'templates/section1/item.html',
        controller: Section1ItemCtrl,
        dependencies: ['id']}).
    within().
        segment('overview', {
            templateUrl: 'templates/section1/item/overview.html'}).

HTML верхнього рівня:

<ul>
    <li ng-class="{active: $routeSegment.startsWith('s1')}">
        <a href="/section1">Section 1</a>
    </li>
    <li ng-class="{active: $routeSegment.startsWith('s2')}">
        <a href="/section2">Section 2</a>
    </li>
</ul>
<div id="contents" app-view-segment="0"></div>

Вкладений HTML:

<h4>Section 1</h4>
Section 1 contents.
<div app-view-segment="1"></div>

Артеме, твоє найкраще рішення, яке я знайшов на сьогодні! Мені подобається те, що ви розширюєте кутовий маршрут, а не заміняєте його, як це робить кутовий інтерфейс.
— LastTribunal

17

Я теж боровся з вкладеними видами в Angular.

Як тільки я отримав інтерфейс маршрутизатора, я зрозумів, що ніколи не повертаюся до функції кутового маршрутизації за замовчуванням.

Ось приклад програми, яка використовує кілька рівнів розміщення поглядів

app.config(function ($stateProvider, $urlRouterProvider,$httpProvider) {
// navigate to view1 view by default
$urlRouterProvider.otherwise("/view1");

$stateProvider
    .state('view1', {
        url: '/view1',
        templateUrl: 'partials/view1.html',
        controller: 'view1.MainController'
    })
    .state('view1.nestedViews', {
        url: '/view1',
        views: {
            'childView1': { templateUrl: 'partials/view1.childView1.html' , controller: 'childView1Ctrl'},
            'childView2': { templateUrl: 'partials/view1.childView2.html', controller: 'childView2Ctrl' },
            'childView3': { templateUrl: 'partials/view1.childView3.html', controller: 'childView3Ctrl' }
        }
    })

    .state('view2', {
        url: '/view2',
    })

    .state('view3', {
        url: '/view3',
    })

    .state('view4', {
        url: '/view4',
    });
});

Як видно, є 4 основні види (view1, view2, view3, view4), а view1 має 3 дитячих подання.


2
Яка мета введення $httpProvider? Я не бачу, що він використовується ніде.
— Аарон

@Aaron app.config не закінчується в цьому фрагменті коду, і може бути $ httpProvider використовується деінде
— Влад

4

Ви можете використовувати ng-include, щоб не використовувати вкладені ng-представлення.

http://docs.angularjs.org/api/ng/directive/ngInclude
http://plnkr.co/edit/ngdoc:example-example39@snapshot?p=preview

Мою індексну сторінку я використовую ng-view. Потім на моїх підсторінках, які мені потрібно мати вкладені кадри. Я використовую ng-include. Демонстрація демонструє спадне меню. Я замінив шахту на посилання ng-click. У функцію я поставив би $ range.template = $ range.templates [0]; або $ range.template = $ range.templates [1];

$scope.clickToSomePage= function(){
  $scope.template = $scope.templates[0];
};

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.