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


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.