AngularJS – UI-router – Come configurare viste dinamiche

Ho trovato difficilmente la documentazione sull’utilizzo dinamico del router tramite un database. Par per il corso, tutto è difficile da codificare.

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": "[email protected]", "templateUrl": "/app/views/content1.html"}, {"name" :"[email protected]", "templateUrl": "/app/views/left1.html" } ] }, { "name": "about", "url": "/about", "abstract": false, "parent": "root", "views": [ {"name": "[email protected]", "templateUrl": "/app/views/content2.html"}, {"name" :"[email protected]", "templateUrl": "/app/views/left2.html" } ] } ] 

La mia app:

 '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 '[email protected]': { 'templateUrl': '/app/views/left1.html' }, '[email protected]': { 'templateUrl': '/app/views/container1.html' }, // third loop '[email protected]': { 'templateUrl': '/app/views/left2.html' }, '[email protected]': { 'templateUrl': '/app/views/container2.html' }, } }); }); $state.go("home"); }); }]); 

Sto avendo difficoltà a configurare le mie viste in modo dinamico. Qualche idea?


AGGIORNARE:

Ho fatto una risposta Plunker per Radim Köhler per chiunque fosse interessato. Apprezzo l’aiuto.

Penso che l’ui-router sia il router defacto per angular e, essendo dinamico, renderà molto più facile gestire una grande app.

C’è un plunker che mostra come possiamo configurare le viste in modo dinamico. La versione aggiornata di .run() sarebbe così:

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

Controlla che tutto sia in azione qui

Devo aggiungere una versione migliorata, quella che è persino in grado di fare di più.

Quindi, ora caricheremo gli stati in modo dinamico, usando $http , json e definiamo gli stati in .run()

Ma ora possiamo navigare verso qualsiasi stato dinamico con url (basta inserirlo nella barra degli indirizzi).

La magia è integrata nell’UI-Router – vedi questa parte di doc:

$ urlRouterProvider

Il deferIntercept(defer)

Disabilita (o abilita) il differimento dell’intercettazione della modifica della posizione.

Se si desidera personalizzare il comportamento della sincronizzazione dell’URL (ad esempio, se si desidera rinviare una transizione mantenendo l’URL corrente) , chiamare questo metodo al momento della configurazione. Quindi, in fase di esecuzione, chiama $urlRouter.listen() dopo aver configurato il proprio gestore di eventi $ locationChangeSuccess.

Frammento citato:

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

Quindi il plunker aggiornato è qui . Ora possiamo usare anche il .otherwise() per navigare allo stato definito di recente, o andare lì per 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(); }); }]); 

Controlla il plunker aggiornato qui

Ho studiato diversi approcci per l’aggiunta dynamic di percorsi a ui.router .

Prima di tutto ho trovato repositori con ngRouteProvider e l’idea principale di esso, sta risolvendo $stateProvider e lo salviamo in chiusura su .config phase e poi uso questa refferenza in qualsiasi altro momento per aggiungere nuovi rotte al volo. È una buona idea e potrebbe essere migliorata. Penso che una soluzione migliore sia quella di dividere la responsabilità per il salvataggio dei riferimenti dei fornitori e la loro manipolazione in qualsiasi momento e luogo. Guarda l’esempio di 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; }; } 

Utilizzando 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 e MenuFactory sono luoghi comuni per la dichiarazione / caricamento di tutti gli stati.

Un’altra idea è che si utilizzano solo fasi di app angolari e si estendono gli stati delle app da parte di provider aggiuntivi, ad esempio:

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

In questo caso non è necessario utilizzare la fase .run per gli stati AppRouts e le AppRouts dell’esempio precedente verranno modificate in:

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

Inoltre abbiamo ancora accesso a tutte le voci di menu in qualsiasi posto: SideMenu.items