AngularJS динамическая маршрутизация


88

Сейчас у меня есть приложение AngularJS со встроенной маршрутизацией. Оно работает, и все в порядке.

Мой файл app.js выглядит так:

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

В мое приложение встроена CMS, где вы можете копировать и добавлять новые html-файлы в каталог / pages .

Я хотел бы по-прежнему пройти через поставщика маршрутизации, хотя даже для новых динамически добавляемых файлов.

В идеальном мире схема маршрутизации была бы такой:

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

Так что, если бы мое новое имя страницы было «contact.html», я бы хотел, чтобы angular взял «/ contact» и перенаправил на «/pages/contact.html».

Возможно ли такое ?! и если да, то как ?!

Обновить

Теперь у меня есть это в моей конфигурации маршрутизации:

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

и в моем CMSController:

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

Это устанавливает для текущего templateUrl правильное значение.

Однако теперь я хотел бы изменить ng-view с новым значением templateUrl. Как это достигается?

Ответы:


133
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'
        });
    }
]);
  • Добавление * позволяет динамически работать с несколькими уровнями каталогов . Пример: / page / cars / sales / list будет улавливаться этим провайдером

Из документов (1.3.0):

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

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

Также

when (путь, маршрут): Метод

  • path может содержать именованные группы, начинающиеся с двоеточия и заканчивающиеся звездочкой: например: name *. Когда маршрут совпадает, все символы с готовностью сохраняются в $ routeParams под заданным именем.

5
Необходимо идти в ногу со временем ... функциональность, которую я создал, которой не хватало в версии 1.0.2, теперь доступна. Обновление принятого ответа на этот
— Грег

Честно говоря, у меня никогда не получалось, что это работает с текущими бета-
— версиями

На самом деле он заработал, когда я сделал это ... when ('/: page', {templateUrl: function (parameters) {return parameters.page + '.html';}
— Архимед Траяно

Серьезно ... Это действительно глупо, что Angular не использует его по умолчанию ... Эта ручная маршрутизация просто смешна
— Гильерме Феррейра

3
Поясните, пожалуйста, откуда urlattrставится или отправляется, то есть что urlattr.nameбудет производить?
— Sami

37

Хорошо решил это.

Добавлено решение в GitHub - http://gregorypratt.github.com/AngularDynamicRouting

В моей конфигурации маршрутизации app.js:

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

Затем в моем контроллере 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'];

Поскольку #views являются моими <div id="views" ng-view></div>

Итак, теперь он работает со стандартной маршрутизацией и динамической маршрутизацией.

Чтобы проверить это, я скопировал файл about.html, назвал его портфолио.html, изменил его содержимое и ввел /#/pages/portfolioв свой браузер, и на экране появилось сообщение hey prestofolio.html ....

Обновлено Добавлены $ apply и $ compile в html, чтобы можно было вводить динамический контент.


2
Это работает только со статическим контентом, если я правильно понимаю. Это связано с тем, что вы изменяете DOM после создания экземпляра контроллера через jQuery (за пределами области действия angular). Такие переменные, как {{this}}, могут работать (я бы попробовал), но директивы, скорее всего, нет. Остерегайтесь этого, так как это может быть полезно сейчас, но может взломать код позже ..
— Тьяго Рольдао,

Правда, привязка не работает, но я не слишком волнуюсь, так как хочу, чтобы клиенты могли только добавлять новые страницы. Любые страницы, которые я добавляю, я бы указал соответствующим образом в конфигурации маршрута. Но хороший момент.
— Грег

1
Это неплохое решение, если вы хотите отображать статический html-контент. Пока вы помните, вы по существу замещаете ng-view статическим (не угловым) контентом. Имея это в виду, было бы проще разделить их, создав что-то вроде элемента <div id = "user-views"> </div> и вставив туда свой "пользовательский шаблон". Кроме того, нет абсолютно никакого смысла в переопределении $ route.current.templateUrl - создании модели $ scope.userTemplate и выполнении $ ('# user-views "). Load ($ scope.userTemplate) чище и не нарушает ни один из angular. код
— Tiago Roldão

1
Нет - директива ng-view довольно ограничена - или, лучше сказать, она предназначена для использования с собственной системой маршрутизации (которая, в свою очередь, все еще ограничена, imho). Вы можете сделать, как вы сказали, внедрить контент в элемент DOM, а затем вызвать метод $ compile, чтобы сообщить angular о необходимости перечитать структуру. Это вызовет любые необходимые привязки.
— Тьягу Рольдао,

2
Работает, но вам не следует выполнять манипуляции с DOM в вашем контроллере.
— dmackerman

16

Я думаю, что самый простой способ сделать это - разрешить маршруты позже, например, вы можете запросить маршруты через json. Обратите внимание, что я создаю фабрику из $ routeProvider на этапе настройки с помощью $ provide, поэтому я могу продолжать использовать объект $ routeProvider на этапе выполнения и даже в контроллерах.

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

$routeProviderИспользование псевдонима в качестве factory(доступного после config) не будет хорошо работать с безопасностью и связностью приложений - в любом случае, крутая техника (голос за!).
— Коди

@Cody, не могли бы вы объяснить примечание о безопасности / связности приложений? Мы находимся в похожей лодке, и что-то вроде приведенного выше ответа было бы действительно кстати.
— virtualandy

2
@virtualandy, как правило, это не лучший подход, поскольку вы делаете поставщика доступным на разных этапах, что, в свою очередь, может привести к утечке утилит высокого уровня, которые должны быть скрыты на этапе настройки. Я предлагаю использовать UI-Router (решает множество проблем), использовать «resolve», «controllerAs» и другие возможности, такие как установка templateUrl для функции. Я также создал модуль «предварительного решения», которым я могу поделиться и который может $ интерполировать любую часть схемы маршрута (шаблон, templateUrl, контроллер и т. Д.). Приведенное выше решение более элегантно, но что вас беспокоит?
— Cody

1
@virtualandy, вот модуль для шаблонов lazyLoaded, контроллеров и т.д. - извиняюсь, его нет в репо, но вот скрипка: jsfiddle.net/cCarlson/zdm6gpnb ... Этот lazyLoads выше - И - может интерполировать что угодно на основе параметров URL. Над модулем можно поработать, но это, по крайней мере, отправная точка.
— Cody

@Cody - без проблем, спасибо за образец и дальнейшие объяснения. Обретите смысл. В нашем случае мы используем angular-route-segment, и он отлично работает. Но теперь нам необходимо поддерживать «динамические» маршруты (некоторые развертывания могут не иметь полных функций / маршрутов страницы или могут изменять свои имена и т. Д.), И мы думали, что идея загрузки в некотором JSON, который определяет маршруты до фактической реализации, была но, очевидно, столкнулись с проблемами при использовании провайдеров $ http / $ в .config ().
— virtualandy

7

В шаблонах URI $ routeProvider вы можете указать переменные параметры, например:, $routeProvider.when('/page/:pageNumber' ...и получить к нему доступ в своем контроллере через $ routeParams.

В конце страницы $ route есть хороший пример: http://docs.angularjs.org/api/ng.$route

РЕДАКТИРОВАТЬ (для отредактированного вопроса):

Система маршрутизации, к сожалению, очень ограничена - по этой теме ведется много дискуссий, и были предложены некоторые решения, а именно создание нескольких именованных представлений и т. Д. Но прямо сейчас директива ngView обслуживает только ОДНО представление для каждого маршрута на взаимно однозначная основа. Вы можете сделать это несколькими способами - проще было бы использовать шаблон представления в качестве загрузчика с <ng-include src="myTemplateUrl"></ng-include>тегом в нем ($ scope.myTemplateUrl будет создан в контроллере).

Я использую более сложное (но более чистое, для более крупных и сложных проблем) решение, в основном полностью пропуская службу $ route, которая подробно описана здесь:

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


Мне нравится этот ng-includeметод. Это действительно просто, и это намного лучший подход, чем манипулирование DOM.
— Нил

5

Не уверен, почему это работает, но динамические (или подстановочные знаки, если хотите) маршруты возможны в 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 -> загружает частичную "foo"

example.com/bar-> загружает "бар" частично

Никаких настроек в ng-view не требуется. Случай '/: a' - единственная переменная, которую я обнаружил, которая добьется этого. '/: Foo' не будет работать, если все ваши частичные данные не являются foo1, foo2 и т.д ... '/: a' работает с любыми частичными название.

Все значения запускают динамический контроллер - так что нет никакого «другого», но я думаю, что это то, что вы ищете в сценарии динамической или групповой маршрутизации.


2

Начиная с AngularJS 1.1.3, вы можете делать именно то, что хотите, используя новый универсальный параметр.

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

Из коммита:

Это позволяет routeProvider принимать параметры, соответствующие подстрокам, даже если они содержат косую черту, если они начинаются со звездочки вместо двоеточия. Например, маршруты вроде edit/color/:color/largecode/*largecode будут соответствовать примерно этому http://appdomain.com/edit/color/brown/largecode/code/with/slashs.

Я тестировал это сам (используя 1.1.5), и он отлично работает. Просто имейте в виду, что каждый новый URL-адрес перезагружает ваш контроллер, поэтому для сохранения любого состояния вам может потребоваться использовать настраиваемую службу.


0

Вот еще одно решение, которое хорошо работает.

(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;

    }

})();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.