Динамічна маршрутизація 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: '/' });
  }]);

У моєму додатку вбудована система управління вмістом, в якій ви можете копіювати та додавати нові файли html в каталог / pages .

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

В ідеальному світі шаблон маршрутизації буде таким:

$ routeProvider.when ('/ назва сторінки ', {templateUrl: '/ сторінки / назва сторінки .html', контролер: 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. Як це досягається?

Відповіді:


132
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 / продажу / список буде ловити цього постачальника

З документів (1.3.0):

"Якщо templateUrl є функцією, вона буде викликана з такими параметрами:

{Array.} - параметри маршруту, витягнуті з поточного $ location.path (), застосовуючи поточний маршрут "

Також

коли (шлях, маршрут): Метод

  • шлях може містити іменовані групи, що починаються двокрапкою і закінчуються зіркою: наприклад: ім'я *. Усі символи охоче зберігаються в $ routeParams під вказаною назвою, коли маршрут збігається.

5
Потрібно рухатись у часі ... функціональність, яку я створив, відсутня у версії 1.0.2, тепер доступна. Оновлення прийнятої відповіді на цю
Грег

Чесно кажучи, я ніколи не працював із нинішніми бета-версіями 1.3
Архімед Траяно

Насправді це спрацювало, коли я зробив це ... коли ('/: сторінка', {templateUrl: функція (параметри) {return parameters.page + '.html';}
Архімед Траяно

Серйозно .. Дуже дурно, що Angular не вважає це поведінкою за замовчуванням ... Ця ручна маршрутизація смішна
Гілерме Феррейра

3
Поясніть, будь ласка, звідки urlattrвстановлюється чи надсилається, я маю на увазі, що urlattr.nameдасть?
Самі

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, назвавши його portfolio.html, змінив частину вмісту та ввів /#/pages/portfolio до мого браузера, і було показано, що presto portfolio.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) чистіший і не порушує жодного з кутових код
Тіаго Ролдао

1
Ні - директива ng-view досить обмежена - або, краще сказати, вона специфічна для використання з власною системою маршрутизації (яка, в свою чергу, все ще обмежена, imho). Ви можете зробити те, що сказали, вводячи вміст у елемент DOM, а потім викликати метод $ compile, щоб сказати angular перечитати структуру. Це спричинить будь-які прив’язки, які потрібно зробити.
Tiago Roldão

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 (опікується великою кількістю труднощів), використовувати "Resolution", "controllerAs" та інші здібності, такі як встановлення templateUrl для функції. Я також створив модуль "presolve", яким я можу поділитися, який може $ інтерполювати будь-яку частину схеми маршруту (шаблон, templateUrl, контролер тощо). Вищевказане рішення є більш елегантним - але які ваші основні проблеми?
Коді,

1
@virtualandy, ось модуль для шаблонів, контролерів і т.д. lazyLoaded - вибачення не в репо, а ось скрипка: jsfiddle.net/cCarlson/zdm6gpnb ... Цей lazyLoads вище - І - може інтерполювати що завгодно на основі параметрів URL. Модуль міг би використати певну роботу, але це принаймні відправна точка.
Коді

@Cody - не проблема, дякую за зразок та подальші пояснення. Зробити сенс. У нашому випадку ми використовуємо сегмент angular-route-segment, і він працює чудово. Але нам тепер потрібно підтримувати "динамічні" маршрути (деякі розгортання можуть не мати повноцінних функцій / маршрутів сторінки, або можуть змінити їх назви тощо), а поняття завантаження в якомусь JSON, що визначає маршрути, до того, як насправді реалізувати, було нашою думкою але натрапив на проблеми з використанням $ http / $ провайдерів у .config (), очевидно.
virtualandy

7

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

Є хороший приклад в кінці сторінки маршруту $: http://docs.angularjs.org/api/ng.$route

EDIT (для відредагованого питання):

На жаль, система маршрутизації дуже обмежена - на цю тему ведеться багато дискусій, і було запропоновано кілька рішень, а саме шляхом створення декількох іменних подань тощо. Але зараз директива 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

Не знаю, чому це працює, але в кутових 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-> частково завантажує "bar"

Немає необхідності в будь-яких налаштуваннях у ng-поданні. Випадок '/: 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.