web-dev-qa-db-fra.com

AngularJS Dynamic Routing

J'ai actuellement une application AngularJS avec routage intégré. Cela fonctionne et tout va bien.

Mon fichier app.js ressemble à ceci:

angular.module('myapp', ['myapp.filters', 'myapp.services', 'myapp.directives']).
  config(['$routeProvider', function ($routeProvider) {
      $routeProvider.when('/', { templateUrl: '/pages/home.html', controller: HomeController });
      $routeProvider.when('/about', { templateUrl: '/pages/about.html', controller: AboutController });
      $routeProvider.when('/privacy', { templateUrl: '/pages/privacy.html', controller: AboutController });
      $routeProvider.when('/terms', { templateUrl: '/pages/terms.html', controller: AboutController });
      $routeProvider.otherwise({ redirectTo: '/' });
  }]);

Mon application a un CMS intégré dans lequel vous pouvez copier et ajouter de nouveaux fichiers HTML dans le répertoire / pages.

J'aimerais quand même passer par le fournisseur de routage même pour les nouveaux fichiers ajoutés dynamiquement.

Dans un monde idéal, le modèle de routage serait:

$ routeProvider.when ('/ pagename', {templateUrl: '/pages/pagename.html', contrôleur: CMSController});

Donc, si mon nouveau nom de page était "contact.html", je voudrais que angular récupère "/ contact" et redirige vers "/pages/contact.html".

Est-ce seulement possible?! et si oui comment?!

Mise à jour

J'ai maintenant ceci dans ma configuration de routage:

$routeProvider.when('/page/:name', { templateUrl: '/pages/home.html', controller: CMSController })

et dans mon CMSController:

function CMSController($scope, $route, $routeParams) {
    $route.current.templateUrl = '/pages/' + $routeParams.name + ".html";
    alert($route.current.templateUrl);
}
CMSController.$inject = ['$scope', '$route', '$routeParams'];

Ceci définit le templateUrl actuel sur la bonne valeur.

Cependant je voudrais maintenant changer le ng-view avec le nouveau valeur templateUrl. Comment est-ce accompli?

88
Greg
angular.module('myapp', ['myapp.filters', 'myapp.services', 'myapp.directives']).
        config(['$routeProvider', function($routeProvider) {
        $routeProvider.when('/page/:name*', {
            templateUrl: function(urlattr){
                return '/pages/' + urlattr.name + '.html';
            },
            controller: 'CMSController'
        });
    }
]);
  • Ajouter * vous permet de travailler avec plusieurs niveaux de répertoires dynamiquement. Exemple: /page/voitures/vente/liste sera attrapé par ce fournisseur

À partir de la documentation (1.3.0):

"Si templateUrl est une fonction, elle sera appelée avec les paramètres suivants:

{Tableau.} - Paramètres de route extraits de la $ location.path () actuelle en appliquant la route actuelle "

Aussi

when (path, route): Méthode

  • path peut contenir des groupes nommés commençant par un signe deux-points et se terminant par une étoile: par exemple: nom*. Tous les caractères sont stockés dans $ routeParams sous le nom indiqué lorsque l'itinéraire correspond.
131
Robin Rizvi

Ok l'a résolu.

Ajout de la solution à GitHub - http://gregorypratt.github.com/AngularDynamicRouting

Dans ma configuration de routage app.js:

$routeProvider.when('/pages/:name', {
    templateUrl: '/pages/home.html', 
    controller: CMSController 
});

Puis dans mon contrôleur CMS:

function CMSController($scope, $route, $routeParams) {

    $route.current.templateUrl = '/pages/' + $routeParams.name + ".html";

    $.get($route.current.templateUrl, function (data) {
        $scope.$apply(function () {
            $('#views').html($compile(data)($scope));
        });
    });
    ...
}
CMSController.$inject = ['$scope', '$route', '$routeParams'];

Avec #views étant mon <div id="views" ng-view></div>

Donc maintenant, cela fonctionne avec le routage standard et le routage dynamique.

Pour le tester, j'ai copié about.html, appelé portfolio.html, modifié une partie de son contenu et entré /#/pages/portfolio dans mon navigateur. Et hop, portfolio.html était affiché ....

Mis à jour Ajout de $ apply et $ compile au code HTML pour que dynamique le contenu peut être injecté.

37
Greg

Je pense que le moyen le plus simple de procéder est de résoudre les itinéraires plus tard. Vous pouvez par exemple les demander via json. Vérifiez que je fabrique une usine à partir de $ routeProvider pendant la phase de configuration, via $ supply, afin de pouvoir continuer à utiliser l'objet $ routeProvider pendant la phase d'exécution et même dans les contrôleurs.

'use strict';

angular.module('myapp', []).config(function($provide, $routeProvider) {
    $provide.factory('$routeProvider', function () {
        return $routeProvider;
    });
}).run(function($routeProvider, $http) {
    $routeProvider.when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
    }).otherwise({
        redirectTo: '/'
    });

    $http.get('/dynamic-routes.json').success(function(data) {
        $routeProvider.when('/', {
            templateUrl: 'views/main.html',
            controller: 'MainCtrl'
        });
        // you might need to call $route.reload() if the route changed
        $route.reload();
    });
});
16
eazel7

Dans les modèles d'URI $ routeProvider, vous pouvez spécifier des paramètres variables, comme ceci: $routeProvider.when('/page/:pageNumber' ..., et y accéder dans votre contrôleur via $ routeParams.

Il y a un bon exemple à la fin de la page $ route: http://docs.angularjs.org/api/ng.$route

EDIT (pour la question modifiée):

Le système de routage est malheureusement très limité - il y a beaucoup de discussions à ce sujet, et des solutions ont été proposées, notamment via la création de vues nommées multiples, etc. Mais pour le moment, la directive ngView ne sert qu’UNE vue par route, une base individuelle. Vous pouvez vous y prendre de plusieurs manières. La plus simple consiste à utiliser le modèle de la vue comme chargeur, avec une balise <ng-include src="myTemplateUrl"></ng-include> (la portée $ scope.myTemplateUrl serait créée dans le contrôleur).

J'utilise une solution plus complexe (mais plus propre, pour les problèmes plus importants et plus complexes), en sautant fondamentalement le service $ route, qui est détaillée ici:

http://www.bennadel.com/blog/2420-Mapping-AngularJS-Routes-Onto-URL-Parameters-And-Client-Side-Events.htm

7
Tiago Roldão

Vous ne savez pas pourquoi cela fonctionne mais des itinéraires dynamiques (ou génériques si vous préférez) sont possibles dans angular 1.2.0-rc.2 ...

http://code.angularjs.org/1.2.0-rc.2/angular.min.js
http://code.angularjs.org/1.2.0-rc.2/angular-route.min.js

angular.module('yadda', [
  'ngRoute'
]).

config(function ($routeProvider, $locationProvider) {
  $routeProvider.
    when('/:a', {
  template: '<div ng-include="templateUrl">Loading...</div>',
  controller: 'DynamicController'
}).


controller('DynamicController', function ($scope, $routeParams) {
console.log($routeParams);
$scope.templateUrl = 'partials/' + $routeParams.a;
}).

example.com/foo -> charge "foo" partiel

example.com/bar-> charge "bar" partiel

Pas besoin d'ajustements dans la vue ng. Le cas '/: a' est la seule variable que j'aie trouvée qui permette d'y arriver .. '/: foo' ne fonctionne pas si vos partiels sont tous foo1, foo2, etc ... '/: a' fonctionne avec tout partiel Nom.

Toutes les valeurs déclenchent le contrôleur dynamique - il n'y a donc pas de "sinon" mais je pense que c'est ce que vous recherchez dans un scénario de routage dynamique ou générique.

5
Matthew Luchak

Depuis AngularJS 1.1.3, vous pouvez maintenant faire exactement ce que vous voulez en utilisant le nouveau paramètre fourre-tout.

https://github.com/angular/angular.js/commit/7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5

Du commit:

Cela permet à routeProvider d’accepter des paramètres qui correspondent aux sous-chaînes même s’ils contiennent des barres obliques s’ils sont précédés d’un astérisque au lieu de deux-points. Par exemple, des itinéraires tels que edit/color/:color/largecode/*largecode correspondent à quelque chose comme ceci http://appdomain.com/edit/color/brown/largecode/code/with/slashs.

Je l'ai testé moi-même (avec 1.1.5) et cela fonctionne très bien. N'oubliez pas que chaque nouvelle URL rechargera votre contrôleur. Par conséquent, pour conserver tout type d'état, vous devrez peut-être utiliser un service personnalisé.

2
Dave

Voici une autre solution qui fonctionne bien.

(function() {
    'use strict';

    angular.module('cms').config(route);
    route.$inject = ['$routeProvider'];

    function route($routeProvider) {

        $routeProvider
            .when('/:section', {
                templateUrl: buildPath
            })
            .when('/:section/:page', {
                templateUrl: buildPath
            })
            .when('/:section/:page/:task', {
                templateUrl: buildPath
            });



    }

    function buildPath(path) {

        var layout = 'layout';

        angular.forEach(path, function(value) {

            value = value.charAt(0).toUpperCase() + value.substring(1);
            layout += value;

        });

        layout += '.tpl';

        return 'client/app/layouts/' + layout;

    }

})();
0
kevinius