Динамические частичные аргументы в маршрутизации 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
. Вы можете выбрать свой собственный подход.
Примечание:
В то время как функция с атрибутом свойств 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 }));
}
}
});
});