AngularJS-UI-router - comment configurer les vues dynamiques
J'ai eu du mal à trouver de la documentation sur l'utilisation dynamique du ui-router via une base de données. Par pour le cours, tout est codé en dur.
Mon Json:
[
{
"name": "root",
"url": "/",
"parent": "",
"abstract": true,
"views": [
{"name": "header", "templateUrl": "/app/views/header.html"},
{"name" :"footer", "templateUrl": "/app/views/footer.html" }
]
},
{
"name": "home",
"url": "",
"abstract" : false,
"parent": "root",
"views": [
{"name": "container@", "templateUrl": "/app/views/content1.html"},
{"name" :"left@", "templateUrl": "/app/views/left1.html" }
]
},
{
"name": "about",
"url": "/about",
"abstract": false,
"parent": "root",
"views": [
{"name": "container@", "templateUrl": "/app/views/content2.html"},
{"name" :"left@", "templateUrl": "/app/views/left2.html" }
]
}
]
Mon Application:
'use strict';
var $stateProviderRef = null;
var $urlRouterProviderRef = null;
var app = angular.module('app', ['ngRoute', 'ui.router']);
app.factory('menuItems', function ($http) {
return {
all: function () {
return $http({
url: '/app/jsonData/efstates.js',
method: 'GET'
});
}
};
});
app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {
$urlRouterProviderRef = $urlRouterProvider;
$stateProviderRef = $stateProvider;
$locationProvider.html5Mode(false);
$urlRouterProviderRef.otherwise("/");
});
app.run(['$q', '$rootScope', '$state', 'menuItems',
function ($q, $rootScope, $state, menuItems) {
menuItems.all().success(function (data) {
angular.forEach(data, function (value, key) {
$stateProviderRef.state(name = value.name, {
"url": value.url,
"parent" : value.parent,
"abstract": value.abstract,
"views": {
// do not want the below hard coded, I want to loop
// through the respective json array objects and populate state & views
// I can do this with everything but views.
// first loop
'header': { 'templateUrl': '/app/views/header.html' },
'footer': { 'templateUrl': '/app/views/footer.html' },
// second loop
'left@': { 'templateUrl': '/app/views/left1.html' },
'container@': { 'templateUrl': '/app/views/container1.html' },
// third loop
'left@': { 'templateUrl': '/app/views/left2.html' },
'container@': { 'templateUrl': '/app/views/container2.html' },
}
});
});
$state.go("home");
});
}]);
J'ai du mal à configurer dynamiquement mes vues. Des idées?
Mise à jour:
J'ai fait un Plunker par la réponse de Radim Köhler pour toute personne intéressée. J'apprécie l'aide.
Je pense que ui-router est le fait routeur angulaire et en étant dynamique, il fera une grande application beaucoup plus facile à gérer.
3 réponses
Il y a un plunker montrant comment nous pouvons configurer les vues dynamiquement. La version mise à jour du .run()
serait comme ceci:
app.run(['$q', '$rootScope', '$state', '$http',
function ($q, $rootScope, $state, $http)
{
$http.get("myJson.json")
.success(function(data)
{
angular.forEach(data, function (value, key)
{
var state = {
"url": value.url,
"parent" : value.parent,
"abstract": value.abstract,
"views": {}
};
// here we configure the views
angular.forEach(value.views, function (view)
{
state.views[view.name] = {
templateUrl : view.templateUrl,
};
});
$stateProviderRef.state(value.name, state);
});
$state.go("home");
});
}]);
Vérifiez que tout est en action ici
Je dois ajouter une version améliorée, celle qui est même capable de faire plus.
Donc, maintenant nous allons toujours charger les États dynamiquement-en utilisant $http
, json
et définir les états dans .run()
Mais maintenant nous pouvons naviguer vers n'importe quel état dynamique avec url (Il suffit de le placer dans la barre d'adresse).
La magie est intégrée dans L'interface utilisateur-Routeur-voir cette partie de doc:
$urlRouterProvider
L'
deferIntercept(defer)
Désactive (ou active) le report de l'interception des changements d'emplacement.
Si vous souhaitez personnaliser le comportement de la synchronisation de l'URL (par exemple, si vous souhaitez reporter une transition, mais de maintenir l'URL), appelez cette méthode au moment de la configuration. Ensuite, au moment de l'exécution, appelez
$urlRouter.listen()
Après avoir configuré votre propre gestionnaire d'événements $ locationChangeSuccess.
Extrait cité:
var app = angular.module('app', ['ui.router.router']);
app.config(function($urlRouterProvider) {
// Prevent $urlRouter from automatically intercepting URL changes;
// this allows you to configure custom behavior in between
// location changes and route synchronization:
$urlRouterProvider.deferIntercept();
}).run(function($rootScope, $urlRouter, UserService) {
$rootScope.$on('$locationChangeSuccess', function(e) {
// UserService is an example service for managing user state
if (UserService.isLoggedIn()) return;
// Prevent $urlRouter's default handler from firing
e.preventDefault();
UserService.handleLogin().then(function() {
// Once the user has logged in, sync the current URL
// to the router:
$urlRouter.sync();
});
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.listen();
});
Donc, le plunker mis à jour est ici. Nous pouvons maintenant utilisez même le .otherwise()
pour naviguer jusqu'à l'état défini récemment, ou allez - y par url:
Le .config()
phase
app.config(function ($locationProvider, $urlRouterProvider, $stateProvider) {
// Prevent $urlRouter from automatically intercepting URL changes;
// this allows you to configure custom behavior in between
// location changes and route synchronization:
$urlRouterProvider.deferIntercept();
$urlRouterProvider.otherwise('/other');
$locationProvider.html5Mode({enabled: false});
$stateProviderRef = $stateProvider;
});
Le .run()
phase
app.run(['$q', '$rootScope','$http', '$urlRouter',
function ($q, $rootScope, $http, $urlRouter)
{
$http
.get("myJson.json")
.success(function(data)
{
angular.forEach(data, function (value, key)
{
var state = {
"url": value.url,
"parent" : value.parent,
"abstract": value.abstract,
"views": {}
};
angular.forEach(value.views, function (view)
{
state.views[view.name] = {
templateUrl : view.templateUrl,
};
});
$stateProviderRef.state(value.name, state);
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.sync();
$urlRouter.listen();
});
}]);
Vérifiez le plunker mis à jour ici
J'ai étudié différentes approches pour ajouter des routes dynamiques à ui.router
.
Tout d'Abord, j'ai trouvé repositori avec ngRouteProvider et l'idée principale, c'est de la résolution de $stateProvider
sna enregistrer dans la fermeture .config
phase et ensuite utiliser cette refference dans le temps, pour ajouter de nouveaux tracés à la volée. C'est une bonne idée, et il peut être amélioré. Je pense que la meilleure solution est de diviser la responsabilité de sauver les références des fournisseurs et de les manipuler à n'importe quel autre moment et lieu. Regardez l'exemple de refsProvider
:
angular
.module('app.common', [])
.provider('refs', ReferencesProvider);
const refs = {};
function ReferencesProvider() {
this.$get = function () {
return {
get: function (name) {
return refs[name];
}
};
};
this.injectRef = function (name, ref) {
refs[name] = ref;
};
}
En utilisant refsProvider
:
angular.module('app', [
'ui.router',
'app.common',
'app.side-menu',
])
.config(AppRouts)
.run(AppRun);
AppRouts.$inject = ['$stateProvider', '$urlRouterProvider', 'refsProvider'];
function AppRouts($stateProvider, $urlRouterProvider, refsProvider) {
$urlRouterProvider.otherwise('/sign-in');
$stateProvider
.state('login', {
url: '/sign-in',
templateUrl: 'tpl/auth/sign-in.html',
controller: 'AuthCtrl as vm'
})
.state('register', {
url: '/register',
templateUrl: 'tpl/auth/register.html',
controller: 'AuthCtrl as vm'
});
refsProvider.injectRef('$urlRouterProvider', $urlRouterProvider);
refsProvider.injectRef('$stateProvider', $stateProvider);
}
AppRun.$inject = ['SectionsFactory', 'MenuFactory', 'refs'];
function AppRun(sectionsFactory, menuFactory, refs) {
let $stateProvider = refs.get('$stateProvider');
// adding new states from different places
sectionsFactory.extendStates($stateProvider);
menuFactory.extendStates($stateProvider);
}
SectionsFactory
et {[10] } est des lieux communs pour la déclaration / chargement de tous les États.
Autre idée, il utilise simplement des phases d'application angulaires existantes et étend les États d'application par des fournisseurs supplémentaires, quelque chose comme:
angular.module('app.side-menu', []);
.provider('SideMenu', SideMenuProvider);
let menuItems = [
{
label: 'Home',
active: false,
icon: 'svg-side-menu-home',
url: '/app',
state: 'app',
templateUrl: 'tpl/home/dasboard.html',
controller: 'HomeCtrl'
}, {
label: 'Charts',
active: false,
icon: 'svg-side-menu-chart-pie',
url: '/charts',
state: 'charts',
templateUrl: 'tpl/charts/main-charts.html'
}, {
label: 'Settings',
active: false,
icon: 'svg-side-menu-settings',
url: '/'
}
];
const defaultExistState = menuItems[0].state;
function SideMenuProvider() {
this.$get = function () {
return {
get items() {
return menuItems;
}
};
};
this.extendStates = ExtendStates;
}
function ExtendStates($stateProvider) {
menuItems.forEach(function (item) {
if (item.state) {
let stateObj = {
url: item.url,
templateUrl: item.templateUrl,
controllerAs: 'vm'
};
if (item.controller) {
stateObj.controller = `${item.controller} as vm`;
}
$stateProvider.state(item.state, stateObj);
} else {
item.state = defaultExistState;
}
});
}
Dans ce cas, nous n'avons pas besoin d'utiliser .run
phase pour les États extenfing, et AppRouts
de l'exemple ci-dessus, sera changé en:
AppRouts.$inject = ['$stateProvider', '$urlRouterProvider', 'SideMenuProvider'];
function AppRouts($stateProvider, $urlRouterProvider, SideMenuProvider) {
$urlRouterProvider.otherwise('/sign-in');
$stateProvider
.state('login', {
url: '/sign-in',
templateUrl: 'tpl/auth/sign-in.html',
controller: 'AuthCtrl as vm'
})
.state('register', {
url: '/register',
templateUrl: 'tpl/auth/register.html',
controller: 'AuthCtrl as vm'
})
SideMenuProvider.extendStates($stateProvider);
}
Aussi, nous avons toujours accès à tous les éléments de menu en tout lieu: SideMenu.items