AngularJS – UI-router – Cómo configurar vistas dinámicas

He tenido dificultades para encontrar cualquier documentación sobre la utilización del ui-router de forma dinámica a través de una base de datos. Par para el curso, todo está codificado.

My 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" } ] } ] 

Mi aplicación:

 '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"); }); }]); 

Estoy teniendo dificultades para configurar mis puntos de vista de forma dinámica. ¿Algunas ideas?


ACTUALIZAR:

Hice una respuesta de Plunker por Radim Köhler para cualquier persona interesada. Aprecio la ayuda.

Creo que ui-router es el enrutador de facto para angular y, por ser dynamic, hará que una aplicación grande sea mucho más fácil de administrar.

Hay un plunker que muestra cómo podemos configurar las vistas dinámicamente. La versión actualizada de .run() sería así:

 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"); }); }]); 

Comprueba que todo en acción aquí

Debo agregar una versión mejorada, la que incluso puede hacer más.

Por lo tanto, ahora todavía cargaremos estados dinámicamente, utilizando $http , json y definiremos estados en .run()

Pero ahora podemos navegar a cualquier estado dynamic con url (simplemente colóquelo en la barra de direcciones).

La magia está incorporada en el UI-Router: mira esta parte del documento:

$ urlRouterProvider

deferIntercept(defer)

Deshabilita (o habilita) la intercepción de cambio de ubicación diferida.

Si desea personalizar el comportamiento de la sincronización de la URL (por ejemplo, si desea diferir una transición pero mantiene la URL actual) , llame a este método en el momento de la configuración. Luego, en tiempo de ejecución, llame a $urlRouter.listen() después de haber configurado su propio controlador de eventos $ locationChangeSuccess.

Fragmento citado:

 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(); }); 

Entonces el plunker actualizado está aquí . Ahora podemos usar incluso .otherwise() para navegar al estado definido recientemente, o ir allí por url:

La fase .config()

 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; }); 

La fase .run()

 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(); }); }]); 

Compruebe el plunker actualizado aquí

Investigué diferentes enfoques para agregar rutas dinámicas a ui.router .

Antes que nada encontré repositorys con ngRouteProvider y la idea principal de esto es resolver $stateProvider y guardarlo en el cierre en la fase .config y luego usar esta referencia en cualquier otro momento para agregar nuevas rutas sobre la marcha. Es una buena idea y puede mejorarse. Creo que una mejor solución es dividir la responsabilidad de guardar las referencias de los proveedores y manipularlas en cualquier otro momento y lugar. Mire el ejemplo 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; }; } 

Usando 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 y MenuFactory son lugares comunes para declarar / cargar todos los estados.

Otra idea es simplemente utilizar las fases angulares de la aplicación y extender los estados de la aplicación por proveedores adicionales, algo así como:

 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; } }); } 

En este caso, no es necesario utilizar .run phase para extender los estados, y AppRouts del ejemplo anterior, se cambiará a:

 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); } 

También tenemos acceso a todos los elementos del menú en cualquier lugar: SideMenu.items