零件和模板的复杂嵌套


503

我的问题涉及到如何在AngularJS应用程序中处理模板的复杂嵌套(也称为partials)。

描述我的情况的最好方法是使用创建的图像:

AngularJS页面图

如您所见,这有可能成为具有许多嵌套模型的相当复杂的应用程序。

该应用程序是单页的,因此它将加载index.html,该索引包含DOM中具有ng-view属性的div元素。

对于第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,因此我不确定如何执行此操作。

我知道我可以在第一个模板中包含其他模板,但是这些模板都将非常复杂。我想将所有模板分开,以使应用程序更易于更新,并且不依赖于必须加载父模板才能访问其子模板。

在第3圈中,您会看到事情变得更加复杂。子导航模板可能会有第二个子导航,需要将其自身的模板以及第4圈的区域加载到该子导航

如何构造一个AngularJS应用程序以处理模板的这种复杂嵌套,同时又将它们彼此分开?


3
如果您仍在关注此线程,我将添加一个指向新AngularUI项目的链接以解决此问题,并添加第三个由子路由提供动力的演示,而无需使用我的回答的指令。
ProLoser 2013年

2
这个如何?bennadel.com/blog/...
ericbae

82
我见过很长时间最漂亮的问题了:)
Mark Nadig

2
同意!喜欢使用模型
Bryan Hong

4
直接在此处的问题中添加指向替代方案的链接将非常有用。github.com/angular-ui/ui-router github.com/artch/angular-route-segment github.com/dotJEM/angular-routing
Jens

Answers:


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 2012年

@blesh,太好了!看起来确实很好解释,但是作为AngularJS的一名优秀JS程序员和初学者,我很难理解这一点...我和社区真的很喜欢使用该方法的JSFiddle链接到工作示例。在周围挖掘它会很容易理解!:)谢谢
Roger Barreto 2013年

8
我认为,对于“嵌套视图无效”的任何问题,此解决方案应该是第一个答案。仅仅因为它更接近于Angular意识形态,而不是使用ui-router等。谢谢。
谢尔盖·潘菲洛夫

那么答案就是指令?
Marc M.

突出显示您可以使用的子导航项:coder1.com/articles/angularjs-managing-active-nav-elements这是一个好方法吗?
escapecat 2014年

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-controller放在每个部分的最顶层


8
我更喜欢您的解决方案。我是Angular的新手,从今天起我对网络的了解似乎更容易理解。谁知道,可能是blesh使用了更多的角度框架来做到这一点,但似乎您以一种更明智的方式用更少的代码行将其钉上了钉子。谢谢!
Gleeb

2
@ProLooser也许您的意思是这个链接github.com/angular-ui/ui-router ...您粘贴的
那个

4
我有与OP相同的设计问题。有没有人尝试过AngularUI状态机制?我不太愿意使用另一个第三方库,而我宁愿坚持使用AngularJS“做事的方式”。但是另一方面,路由系统似乎是其致命弱点……@PhillipKregg,您最终用什么来解决这种情况?
山姆

4
您可以指定<div ng-include="'/home/' + tab + '.html'" ng-controller="SubCtrl"></div>为子模板使用单独的控制器/作用域。或者只是ngController在子模板内的任何地方指定指令,以对每个部分使用不同的控制器。
Colllin

2
@DerekAdair该项目相当稳定(尽管有版本号),并在一些地方用于生产中。它可以防止不必要的控制器重载,并且是我建议的解决方案的更好替代方案。
ProLoser 2013年

26

您也可以出于相同目的签出该库:

http://angular-route-segment.com

它看起来像您要找的东西,并且比ui-router使用起来简单得多。从演示站点

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>

Artem,您的是迄今为止找到的最好的解决方案!我喜欢这样的事实,即您扩展了角度路线,而不是像角度ui那样替换它。
LastTribunal 2014年

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不会在这段代码中结束,可能 是在其他地方使用了$ httpProvider
Vlad

4

您可以使用ng-include来避免使用嵌套的ng-views。

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

我的索引页面使用ng-view。然后在我的子页面上,我需要使用嵌套框架。我使用ng-include。该演示显示了一个下拉列表。我用链接ng-click替换了我的。在函数中,我将$ scope.template = $ scope.templates [0]; 或$ scope.template = $ scope.templates [1];

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.