Ma question concerne la gestion de l’imbrication complexe de templates (également appelé partiels) dans une application AngularJS.
La meilleure façon de décrire ma situation est d'utiliser une image que j'ai créée:
Comme vous pouvez le constater, cette application est potentiellement complexe et comporte de nombreux modèles imbriqués.
L'application est à page unique, elle charge donc un index.html qui contient un élément div dans le DOM avec l'attribut ng-view
.
Pour le cercle 1, vous voyez qu’une navigation principale charge les modèles appropriés dans le ng-view
. Je le fais en passant $routeParams
au module principal de l'application. Voici un exemple de ce que contient mon application:
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' })
}]);
Dans le cercle 2, le modèle chargé dans le ng-view
a un autre sous-navigation. Ce sous-navigateur doit ensuite charger des modèles dans la zone située en dessous - mais, vu que ng-view est déjà utilisé, je ne sais pas comment procéder.
Je sais que je peux inclure des modèles supplémentaires dans le premier modèle, mais ces modèles seront tous assez complexes. Je souhaite que tous les modèles soient séparés afin de rendre l'application plus facile à mettre à jour et de ne pas avoir à dépendre du modèle parent devant être chargé pour pouvoir accéder à ses enfants.
Dans le cercle, vous pouvez voir que les choses deviennent encore plus complexes. Il est possible que les modèles de sous-navigation aient un 2ème sous-navigation qui devra également charger ses propres modèles dans la zone cercle 4
Comment peut-on structurer une application AngularJS pour gérer une imbrication aussi complexe de modèles tout en les gardant séparés les uns des autres?
Eh bien, comme vous ne pouvez actuellement avoir qu’une seule directive ngView ... j’utilise des contrôles de directive imbriqués. Cela vous permet de configurer des modèles et d'hériter (ou d'isoler) des étendues entre eux. En dehors de cela, j'utilise ng-switch ou même simplement ng-show pour choisir les commandes à afficher en fonction de ce qui vient de $ routeParams.
EDIT Voici un exemple de pseudo-code pour vous donner une idée de ce dont je parle. Avec une sous navigation imbriquée.
Voici la page principale de l'application
<!-- 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>
Directive pour la sous navigation
app.directive('mySubNav', function(){
return {
restrict: 'E',
scope: {
current: '=current'
},
templateUrl: 'mySubNav.html',
controller: function($scope) {
}
};
});
modèle pour la sous navigation
<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>
modèle pour une page principale (depuis le navigateur 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>
Contrôleur pour une page principale. (du nav primaire)
app.controller('page1Ctrl', function($scope, $routeParams) {
$scope.sub = $routeParams.sub;
});
Directive pour une sous-zone
app.directive('mySubArea1', function(){
return {
restrict: 'E',
templateUrl: 'mySubArea1.html',
controller: function($scope) {
//controller for your sub area.
}
};
});
Pour les sous-sections, il est aussi simple que de tirer parti des chaînes dans 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>
Ou vous pouvez créer un objet au cas où vous auriez des liens vers des sous-pages partout:
$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>
Ou vous pouvez même utiliser $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>
Vous pouvez également mettre un contrôleur ng au plus haut niveau de chaque partiel
Vous pouvez également acheter cette bibliothèque dans le même but:
http://angular-route-segment.com
Cela ressemble à ce que vous recherchez et il est beaucoup plus simple à utiliser que ui-router. Depuis le site de démonstration :
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 premier niveau:
<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 imbriqué:
<h4>Section 1</h4>
Section 1 contents.
<div app-view-segment="1"></div>
Moi aussi, je me débattais avec des vues imbriquées dans Angular.
Une fois que j’ai eu la main sur i-router je savais que je ne revenais jamais à la fonctionnalité de routage par défaut angular.
Voici un exemple d'application utilisant plusieurs niveaux d'imbrication de vues.
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',
});
});
Comme on peut le voir, il y a 4 vues principales (view1, view2, view3, view4) et view1 a 3 vues enfants.
Vous pouvez utiliser ng-include pour éviter d'utiliser des vues ng imbriquées.
http://docs.angularjs.org/api/ng/directive/ngInclude
http://plnkr.co/edit/ngdoc:example-example39@snapshot?p=preview
Ma page d'index j'utilise ng-view. Ensuite, sur mes sous-pages, il me faut des cadres imbriqués. J'utilise ng-include. La démo montre une liste déroulante. J'ai remplacé le mien par un lien ng-click. Dans la fonction, je mettrais $ scope.template = $ scope.templates [0]; ou $ scope.template = $ scope.templates [1];
$scope.clickToSomePage= function(){
$scope.template = $scope.templates[0];
};
Le routeur ui angulaire prend en charge les vues imbriquées. Je ne l'ai pas encore utilisé mais j'ai l'air très prometteur.