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.

29
demandé sur Radim Köhler 2014-07-14 01:11:29

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

23
répondu Radim Köhler 2014-07-14 06:13:36

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

19
répondu Radim Köhler 2015-03-12 15:30:54

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

4
répondu Pencroff 2015-06-14 00:24:48