Anidamiento complejo de parciales y plantillas.
Mi pregunta es cómo lidiar con el anidamiento complejo de plantillas (también llamados parciales ) en una aplicación AngularJS.
La mejor manera de describir mi situación es con una imagen que creé:
Como puede ver, tiene el potencial de ser una aplicación bastante compleja con muchos modelos anidados.
La aplicación es de una sola página, por lo que carga un index.html que contiene un elemento div en el DOM con el ng-view
atributo.
Para el círculo 1 , verá que hay una navegación principal que carga las plantillas apropiadas en el archivo ng-view
. Hago esto pasando $routeParams
al módulo principal de la aplicación. Aquí hay un ejemplo de lo que hay en mi aplicación:
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' })
}]);
En el círculo 2 , la plantilla que se carga en el tiene una subnavegaciónng-view
adicional . Este subnav luego necesita cargar plantillas en el área debajo de él, pero como ya se está usando ng-view, no estoy seguro de cómo hacerlo.
Sé que puedo incluir plantillas adicionales dentro de la primera plantilla, pero todas estas plantillas serán bastante complejas. Me gustaría mantener todas las plantillas separadas para que la aplicación sea más fácil de actualizar y no depender de que la plantilla principal deba cargarse para poder acceder a sus elementos secundarios.
En el círculo 3 , puedes ver que las cosas se vuelven aún más complejas. Existe la posibilidad de que las plantillas de subnavegación tengan una segunda subnavegación que también necesitará cargar sus propias plantillas en el área del círculo 4.
¿Cómo se puede estructurar una aplicación AngularJS para lidiar con un anidamiento de plantillas tan complejo y al mismo tiempo mantenerlas separadas unas de otras?
ACTUALIZACIÓN: consulte el nuevo proyecto de AngularUI para abordar este problema
Para las subsecciones es tan fácil como aprovechar cadenas en 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>
O puede crear un objeto en caso de que tenga enlaces a subpáginas por todas partes:
$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>
O incluso puedes usar$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>
También puedes poner un controlador ng en el nivel más alto de cada parcial.
Bueno, dado que actualmente solo puedes tener una directiva ngView... uso controles de directiva anidados. Esto le permite configurar plantillas y heredar (o aislar) ámbitos entre ellas. Aparte de eso, uso ng-switch o incluso simplemente ng-show para elegir qué controles estoy mostrando en función de lo que viene de $routeParams.
EDITAR Aquí hay un pseudocódigo de ejemplo para darle una idea de lo que estoy hablando. Con una subnavegación anidada.
Aquí está la página principal de la aplicación.
<!-- 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>
Directiva para la subnavegación
app.directive('mySubNav', function(){
return {
restrict: 'E',
scope: {
current: '=current'
},
templateUrl: 'mySubNav.html',
controller: function($scope) {
}
};
});
plantilla para la subnavegación
<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>
plantilla para una página principal (desde la navegación principal)
<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>
Controlador para una página principal. (desde el navegador principal)
app.controller('page1Ctrl', function($scope, $routeParams) {
$scope.sub = $routeParams.sub;
});
Directiva para una subárea
app.directive('mySubArea1', function(){
return {
restrict: 'E',
templateUrl: 'mySubArea1.html',
controller: function($scope) {
//controller for your sub area.
}
};
});
También puede retirar esta biblioteca con el mismo propósito:
http://segmento-de-ruta-angular.com
Parece lo que estás buscando y es mucho más sencillo de usar que ui-router. Desde el sitio de demostración :
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 de nivel superior:
<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 anidado:
<h4>Section 1</h4>
Section 1 contents.
<div app-view-segment="1"></div>
Yo también estaba luchando con las vistas anidadas en Angular.
Una vez que conseguí ui-router , supe que nunca volvería a la funcionalidad de enrutamiento predeterminada de Angular.
A continuación se muestra una aplicación de ejemplo que utiliza múltiples niveles de anidamiento de vistas.
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',
});
});
Como puede verse, hay 4 vistas principales (vista1, vista2, vista3, vista4) y la vista1 tiene 3 vistas secundarias.