Динамические частичные аргументы в маршрутизации AngularJS

Я работаю с сайтом angularjs и имею опыт работы с маршрутами в Rails, а также Laravel в php. С помощью маршрутов в Laravel мы могли бы динамически создавать набор маршрутов, похожих на:

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

  endforeach;                      

Здесь мы определили ряд отдельных маршрутов в Laravel, которые технически выглядят одинаково, за исключением значений city и slug.

Я нахожу angularJS немного ограниченным в определении маршрутов в этом случае. Честно говоря, я немного потерял здесь.

UPDATE

Здесь я внес некоторые изменения - в основном я создал службу, которая извлекает активы из моей базы данных, например, в этом случае список городов и категорий. Я пытаюсь сделать это:

Если {slug} находится в массиве категорий, извлеченных из моего API, используйте мой ListController и список, но если вместо этого вместо этого используйте мой SingleVenueController и один вид. Здесь мой код на данный момент, но его не работает: (

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

В вышеприведенном sharedParams есть сервис, а getCurrentPageType просто проверяет URL-адрес URL-адреса, чтобы решить, какой контроллер отправить обратно, но он вообще не работает: (

Ответы

Ответ 1

Как определить один маршрут с параметром? В angularjs v1.x вы можете определить столько маршрутов, сколько хотите, с таким количеством параметров 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.org/api/ngRoute/service/ $route

Ответ 2

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

Вы можете сделать это с помощью отдельных маршрутов. Идея заключается в том, когда пользователь набирает основной маршрут, который он разрешает первым, с данными из бэкэнд. Если условие выполнено, функция разрешения перенаправляет на определенный маршрут, если он не пройдет

Ответ 3

Услуги в Angular не могут быть введены на фазе конфигурации, поскольку они становятся доступными только на этапе запуска приложения Angular.

Однако существует трюк для загрузки службы $http на этапе конфигурации, который вы можете использовать для загрузки своих городов/категорий и настройки ваших маршрутов. Между тем, поскольку контроллеры не зарегистрированы до фазы запуска, вы можете использовать $controllerProvider для регистрации ваших контроллеров заранее на этапе конфигурации:

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

Теперь вы можете позвонить в свой API, чтобы получить города (или что-то еще) и выполнить итерацию при регистрации каждого маршрута:

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

Обратите внимание, что я использую методы getTemplate и getController, которые возвращают templateUrl и соответствующие строки имени контроллера соответственно с помощью обычного выражения switch. Вы можете выбрать свой собственный подход.

Демо-версия Plunkr

Примечание:

В то время как функция с атрибутом свойств templateUrl route работает с настройкой настраиваемого шаблона, но когда вы используете функцию рядом с свойством controller, Angular будет рассматривать ее как конструктор для контроллера. Поэтому возврат имени контроллера в эту функцию не будет работать.

Ответ 4

Как уже отмечал Ахмад в своем ответе , если вы передаете функцию в controller, он рассматривается как конструктор для контроллера. Кроме того, вы не можете получить динамическую добавленную услугу в блоке конфигурации вашего приложения.

Итак, что вы можете сделать, переместите свою службу sharedData в отдельное приложение (в моем коде ниже я использовал appShared как отдельное приложение, где эта служба определена), а затем получить доступ к нему с помощью angular.injector. Таким образом, вам не нужно определять его как параметр для функций templateUrl/controller.

Btw, вы не можете передавать пользовательские параметры функции templateUrl (ref: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider)

Если templateUrl является функцией, он будет вызываться со следующим Параметры:

{Array.<Object>} - параметры маршрута, извлеченные из текущего $location.path(), путем применения текущего маршрута

Теперь для контроллера используйте $controller для динамической загрузки либо ListsController, либо SingleController на основе вашего условия. Как только это будет загружено, добавьте текущий контроллер (определенный функцией контроллера) с помощью angular.extend, чтобы он наследовал все свойства и методы динамически загружаемого контроллера.

Проверьте полный код здесь: 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 }));
            }
        }
    });


});