admin管理员组

文章数量:1287581

I'm working with an angularjs site and have a background with working with routes in Rails and also Laravel in php. With routes in Laravel we could dynamically create a set of routes similar to:

  foreach($cities as $city):
    Route::get($city.'/hotels');
    Route::get($city.'/{slug}');

  endforeach;                      

Here we defined series of seperate routes in Laravel which technically do look the same except for the value of city and slug.

I'm finding angularJS a bit limited in defining routes in this case. Frankly am a bit lost here.

UPDATE

I've made some modifications here - basically I set up a service which retrieves assets from my database such as in this case a list of cities and categories. I'm trying to do this:

If {slug} is in the array of categories retrieved from my API, then use my ListController and list view but if its not then instead use my SingleVenueController and single view. Here's my code at the moment but its not working :(

  appRouteProvider.when('/:city/:slug', {
      templateUrl : function(sharedParams, $routeParams){
        t = sharedParams.getCurrentPageType($routeParams);
        if(t=='list'){
          return '../../app/templates/list.html';
        }
        if(t=='single'){
          return '../../app/templates/single.html';
        }

      },
      controller  :  function(sharedParams, $routeParams){
        t = sharedParams.getCurrentPageType($routeParams);
        if(t=='list'){
          return 'ListsController';
        }
        if(t=='single'){
          return 'SingleController';
        }
      },


    resolve:{
      sharedParamsData:function(sharedParams){
        return sharedParams.promise;
      },
    }
  })

In the above sharedParams is a service and the getCurrentPageType just checks the url slug to decide what controller to send back - but its not really working at all :(

I'm working with an angularjs site and have a background with working with routes in Rails and also Laravel in php. With routes in Laravel we could dynamically create a set of routes similar to:

  foreach($cities as $city):
    Route::get($city.'/hotels');
    Route::get($city.'/{slug}');

  endforeach;                      

Here we defined series of seperate routes in Laravel which technically do look the same except for the value of city and slug.

I'm finding angularJS a bit limited in defining routes in this case. Frankly am a bit lost here.

UPDATE

I've made some modifications here - basically I set up a service which retrieves assets from my database such as in this case a list of cities and categories. I'm trying to do this:

If {slug} is in the array of categories retrieved from my API, then use my ListController and list view but if its not then instead use my SingleVenueController and single view. Here's my code at the moment but its not working :(

  appRouteProvider.when('/:city/:slug', {
      templateUrl : function(sharedParams, $routeParams){
        t = sharedParams.getCurrentPageType($routeParams);
        if(t=='list'){
          return '../../app/templates/list.html';
        }
        if(t=='single'){
          return '../../app/templates/single.html';
        }

      },
      controller  :  function(sharedParams, $routeParams){
        t = sharedParams.getCurrentPageType($routeParams);
        if(t=='list'){
          return 'ListsController';
        }
        if(t=='single'){
          return 'SingleController';
        }
      },


    resolve:{
      sharedParamsData:function(sharedParams){
        return sharedParams.promise;
      },
    }
  })

In the above sharedParams is a service and the getCurrentPageType just checks the url slug to decide what controller to send back - but its not really working at all :(

Share Improve this question edited Oct 12, 2016 at 16:48 Sensei James 2,6951 gold badge30 silver badges36 bronze badges asked Sep 27, 2016 at 14:36 AliAli 7,49322 gold badges105 silver badges164 bronze badges 6
  • Are you using ui-router or ng-route? – Merlin Commented Sep 27, 2016 at 14:54
  • 1 well, you can insert a service on config, and then, retrieve the list and maps city by city – Joao Polo Commented Sep 27, 2016 at 14:55
  • ng-route for now - I'm interested in your solution on using a service how do I do that? – Ali Commented Sep 27, 2016 at 15:16
  • I've made amendments to my code - but its not really working right. Can you please give me an idea here? – Ali Commented Oct 5, 2016 at 14:07
  • Are you getting errors? – John Roca Commented Oct 12, 2016 at 3:22
 |  Show 1 more ment

4 Answers 4

Reset to default 6

How about defining a single route with a paramater ? In angularjs v1.x you can defined as many routes you want with as many params xor query

.config(function($routeProvider, $locationProvider) {
  $routeProvider
   .when('/city/:slug', {
    templateUrl: 'book.html',
    controller: 'BookController',
    resolve: {
     // you can also retrieve some data as a resolved promise inside your route for better performance.
  }
})

ref: https://docs.angularjs/api/ngRoute/service/$route

appRouteProvider.when('/:city/:slug', {
templateUrl : 'dafault.html',
controller  :  'DefaultController',
resolve:{
    factory: function($routeParams, $http, $location, sharedParams){
        var city = $routeParams.city;
        var slug = $routeParams.slug;
        var deferred = $q.defer();

        sharedParams.getCurrentPageType($routeParams).then(function(t) {
            if(t=='list'){
                $location.path('/' + city + '/' + slug + '/list');
                deferred.resolve();
            }
            else if(t=='single'){
                $location.path('/' + city + '/' + slug + '/single');
                deferred.resolve();
            } else {
                deferred.reject();
            }

        });

        return deferred.promise;
    },


 }
});
appRouteProvider.when('/:city/:slug/list', {
    templateUrl: '../../app/templates/list.html',
    controller: 'ListsController',
});
appRouteProvider.when('/:city/:slug/single', {
    templateUrl: '../../app/templates/single.html',
    controller: 'SingleController',
});

You can do it with separate routes. The idea is when user hits the main route it resolves first with the data from the backend. If the condition is met, resolve function will redirect to specific route if not it wont pass

Services in Angular cannot be injected in the configuration phase since they bee available only in the run phase of an Angular application.

There is however a trick to load $http service in the config phase which you can use to load your cities/categories and set up your routes. Meanwhile, since controllers aren't registered up until the run phase, you may use the $controllerProvider to register your controllers beforehand in the configuration phase:

app.config(function ($routeProvider, $controllerProvider) {
  $controllerProvider.register('ListController', ListController);
  $controllerProvider.register('SingleController', SingleController);

  // wire the $http service
  var initInjector = angular.injector(['ng']);
  var $http = initInjector.get('$http');
  ...
});

You can now call your API to get the cities (or whatever else) and iterate while registering each route:

  ...
  // fetch the cities from the server
  $http.get('/cities')
  .then(function (response) {
    var cities = response.data;

    for(var i = 0; i < cities.length; i++){
      $routeProvider
      // assuming each city object has a `name` property
      .when('/' + cities[i]['name'] + '/:slug', {
        templateUrl: getTemplate(cities[i]['name']),
        controller: getController(cities[i]['name'])
      })
    }
  });
  ...

Note that I'm using the getTemplate and the getController methods which return the templateUrl and the relevant controller name strings respectively using an ordinary switch expression. You can choose your own approach.

Plunkr Demo

Note:

While a function with the templateUrl route options property does work with setting up a custom template, but when you use a function alongside the controller property, Angular will consider it as the constructor for the controller. Therefore, returning the name of the controller in that function won't work.

As Ahmad has already pointed out in his answer, if you pass a function to controller it is considered as a constructor for the controller. Also you can't get a service injected dynamically in config block of your app.

So what you can do is, move your sharedData service in separate app (in my code below I've used appShared as a separate app where this service is defined) and then access it using angular.injector. This way you don't have to define it as a parameter to templateUrl / controller functions.

Btw, you can't pass custom parameters to templateUrl function (ref: https://docs.angularjs/api/ngRoute/provider/$routeProvider)

If templateUrl is a function, it will be called with the following parameters:

{Array.<Object>} - route parameters extracted from the current $location.path() by applying the current route

Now for the controller, use $controller to dynamically load either ListsController or SingleController based on your condition. Once that is loaded, extend your current controller (defined by your controller function) using angular.extend so that it inherits all the properties and methods of the dynamically loaded controller.

Check the plete code here: http://plnkr.co/edit/ORB4iXwmxgGGJW6wQDy9

app.config(function ($routeProvider) {
    var initInjector = angular.injector(['appShared']);
    var sharedParams = initInjector.get('sharedParams');

    $routeProvider
    .when('/:city/:slug', {
        templateUrl: function ($routeParams) {
            console.log("template url - ");
            console.log($routeParams);
            var t = sharedParams.getCurrentPageType($routeParams);
            console.log(t);
            if (t == 'list') {
                return 'list.html';
            }
            if (t == 'single') {
                return 'single.html';
            }

        },
        controller: function ($routeParams, $controller, $scope) {
            //getController(cities[i]['name'])
            console.log("controller - ");
            console.log($routeParams);
            var t = sharedParams.getCurrentPageType($routeParams);
            console.log(t);
            if (t == 'list') {
                angular.extend(this, $controller('ListsController', { $scope: $scope }));
            }
            if (t == 'single') {
                angular.extend(this, $controller('SingleController', { $scope: $scope }));
            }
        }
    });


});

本文标签: javascriptDynamic partial arguments in AngularJS routingStack Overflow