Сложное вложение партиалов и шаблонов

503

Мой вопрос состоит в том, как решить сложную вложение шаблонов (также называемых частичными ) в приложении AngularJS.

Лучший способ описать мою ситуацию с помощью изображения, которое я создал:

AngularJS Page Diagram

Как видите, это может быть довольно сложным приложением с множеством вложенных моделей.

Приложение является одностраничным, поэтому оно загружает 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' })       

    }]);

В кружке 2 шаблон, который загружен в, ng-viewимеет дополнительную суб-навигацию . Затем эта под-навигация должна загрузить шаблоны в область под ней - но так как ng-view уже используется, я не уверен, как это сделать.

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

В круге 3 вы можете увидеть, что все становится еще сложнее. Существует вероятность того, что шаблоны суб-навигации будут иметь 2-ю суб-навигацию , которая должна будет загружать свои собственные шаблоны также в область в круге 4

Как можно структурировать приложение AngularJS, чтобы справиться с таким сложным вложением шаблонов, сохраняя их все отдельно друг от друга?

PhillipKregg
источник
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
Дженс,

Ответы:

171

Ну, поскольку в настоящее время вы можете иметь только одну директиву ngView ... Я использую вложенные элементы управления директивами. Это позволяет вам устанавливать шаблоны и наследовать (или изолировать) области действия между ними. Помимо этого я использую ng-switch или даже просто ng-show, чтобы выбрать, какие элементы управления я отображаю, основываясь на том, что поступает из $ routeParams.

РЕДАКТИРОВАТЬ Вот несколько примеров псевдокода, чтобы дать вам представление о том, о чем я говорю. С вложенной суб-навигацией.

Вот главная страница приложения

<!-- 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
@bsh, хорошо! Это кажется действительно хорошим объяснением, но как хороший JS-программист и новичок в AngularJS, я могу это хорошо уловить ... Мне и сообществу очень понравится ссылка JSFiddle на рабочий образец с использованием этого подхода. Разобраться в этом будет легко для понимания! :) Спасибо
Роджер Баррето
8
Я думаю, что это решение должно быть первым ответом на любые вопросы типа «вложенное представление не сработало». Просто потому, что это гораздо ближе к идеологии Angular вместо использования ui-router и т. Д. Спасибо.
Сергей Панфилов
Таким образом, ответ директивы?
Марк М.
чтобы выделить элементы под-навигации, вы можете использовать это: coder1.com/articles/angularjs-managing-active-nav-elements это хороший способ сделать это?
escapedcat
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-контроллер на самый верхний уровень каждого частичного

ProLoser
источник
8
Мне нравится ваше решение лучше. Я новичок в Angular, и это выглядит гораздо более понятным с того момента, как я вижу сеть до сегодняшнего дня. кто знает, может быть, Blesh использует больше угловой структуры, чтобы сделать это, но кажется, что вы прибили это с меньшим количеством строк кода более разумным способом. Спасибо!
Глеб
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директиву в любом месте вашего под-шаблона (ов), чтобы использовать разные контроллеры для каждого частичного.
Коллин
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>
artch
источник
Артем, твое лучшее решение, которое я нашел на сегодняшний день! Мне нравится тот факт, что вы расширяете угловой маршрут, а не заменяете его, как это делает угловой интерфейс.
LastTribunal
17

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

Как только я овладел ui-router, я понял, что никогда не вернусь к функциям угловой маршрутизации по умолчанию.

Вот пример приложения, которое использует несколько уровней вложенности представлений

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 не заканчивается этим фрагментом кода и может быть, $ $Provider используется где-то еще
Влад
4

Вы можете использовать ng-include, чтобы избежать использования вложенных ng-views.

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

Моя страница индекса я использую ng-view. Затем на моих подстраницах, которые мне нужно иметь вложенные рамки. Я использую нг-включить. Демо показывает раскрывающийся список. Я заменил мой со ссылкой нг-клик. В функции я бы поместил $ scope.template = $ scope.templates [0]; или $ scope.template = $ scope.templates [1];

$scope.clickToSomePage= function(){
  $scope.template = $scope.templates[0];
};
Генри Мак
источник
2

Angular ui-router поддерживает вложенные представления. Я еще не использовал его, но выглядит очень многообещающе.

http://angular-ui.github.io/ui-router/

Адриан Буман
источник