Định tuyến động AngularJS


88

Tôi hiện có một ứng dụng AngularJS với định tuyến được tích hợp sẵn. Nó hoạt động và mọi thứ đều ổn.

Tệp app.js của tôi trông giống như sau:

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

Ứng dụng của tôi có CMS được tích hợp sẵn để bạn có thể sao chép và thêm các tệp html mới trong thư mục / pages .

Tôi vẫn muốn truy cập nhà cung cấp định tuyến ngay cả đối với các tệp mới được thêm động.

Trong một thế giới lý tưởng, mô hình định tuyến sẽ là:

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

Vì vậy, nếu tên trang mới của tôi là "contact.html", tôi muốn chọn "/ contact" và chuyển hướng đến "/pages/contact.html".

Điều này thậm chí có thể ?! và nếu vậy thì làm thế nào ?!

Cập nhật

Bây giờ tôi có cái này trong cấu hình định tuyến của mình:

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

và trong CMSController của tôi:

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

Điều này đặt templateUrl hiện tại thành giá trị phù hợp.

Tuy nhiên, bây giờ tôi muốn thay đổi ng-view bằng giá trị templateUrl mới. Làm thế nào điều này được hoàn thành?

Câu trả lời:


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'
        });
    }
]);
  • Thêm * cho phép bạn làm việc động với nhiều cấp thư mục . Ví dụ: / trang / ô tô / bán / danh sách sẽ được bắt trên nhà cung cấp này

Từ tài liệu (1.3.0):

"Nếu templateUrl là một hàm, nó sẽ được gọi với các tham số sau:

{Array.} - tham số tuyến đường được trích xuất từ ​​$ location.path () hiện tại bằng cách áp dụng tuyến đường hiện tại "

Cũng thế

when (path, route): Phương thức

  • đường dẫn có thể chứa các nhóm được đặt tên bắt đầu bằng dấu hai chấm và kết thúc bằng dấu sao: ví dụ: tên *. Tất cả các ký tự được lưu trữ trong $ routeParams dưới tên đã cho khi tuyến đường khớp.

5
Cần phải di chuyển theo thời đại ... chức năng tôi đã xây dựng bị thiếu trong v1.0.2 hiện đã có sẵn. Cập nhật câu trả lời được chấp nhận cho câu trả lời này
Greg

Thành thật mà nói tôi không bao giờ làm việc này với 1.3 beta hiện tại
Archimedes Trajano

Trên thực tế đã nhận nó làm việc khi tôi đã làm điều này ... khi ( '/: trang', {templateUrl: function (parameters) {return parameters.page + '.html';}
Archimedes Trajano

Nghiêm túc đấy .. Thật là ngu ngốc khi Angular không có nó như một hành vi mặc định ... Việc định tuyến thủ công đó thật nực cười
Guilherme Ferreira

3
Xin vui lòng giải thích, Nơi từ urlattrđược đặt hoặc gửi, tôi có nghĩa là những gì urlattr.namesẽ sản xuất?
Sami

37

Ok đã giải quyết nó.

Đã thêm giải pháp vào GitHub - http://gregorypratt.github.com/AngularDynamicRouting

Trong cấu hình định tuyến app.js của tôi:

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

Sau đó, trong bộ điều khiển CMS của tôi:

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'];

Với #views là của tôi <div id="views" ng-view></div>

Vì vậy, bây giờ nó hoạt động với định tuyến tiêu chuẩn và định tuyến động.

Để kiểm tra nó, tôi đã sao chép about.html được gọi là portfolio.html, thay đổi một số nội dung của nó và nhập /#/pages/portfoliovào trình duyệt của tôi và hey presto portfolio.html đã được hiển thị ....

Đã cập nhật Đã thêm $ áp dụng và $ biên dịch vào html để có thể đưa nội dung động vào.


2
Điều này chỉ hoạt động với nội dung tĩnh, nếu tôi hiểu nó một cách chính xác. Điều này là do bạn đang thay đổi DOM sau khi đã khởi tạo bộ điều khiển, thông qua jQuery (phạm vi bên ngoài của góc). Các biến như {{this}} có thể hoạt động (tôi sẽ phải thử nó) nhưng các lệnh rất có thể sẽ không. Hãy cảnh giác với điều này, vì nó có thể hữu ích bây giờ, nhưng có thể phá vỡ mã sau ..
Tiago Roldão

Đúng, ràng buộc không hoạt động, tuy nhiên đối với tôi, tôi không quá bối rối vì tôi chỉ muốn khách hàng có thể thêm các trang mới. Bất kỳ trang nào tôi thêm vào, tôi sẽ chỉ định thích hợp thông qua cấu hình tuyến. Tuy nhiên, điểm tốt.
Greg

1
Đó không phải là một giải pháp tồi nếu bạn muốn hiển thị nội dung html tĩnh. Vì vậy, miễn là bạn nhớ rằng về cơ bản bạn đang ghi đè ng-view bằng nội dung tĩnh (không góc cạnh). Với ý nghĩ đó, sẽ dễ dàng hơn nếu tách cả hai, tạo thứ gì đó giống như phần tử <div id = "user-views"> </div> và đưa "mẫu người dùng" của bạn vào đó. Ngoài ra, hoàn toàn không có ý nghĩa gì khi ghi đè $ route.current.templateUrl - tạo mô hình $ scope.userTemplate và thực hiện $ ('# user-views "). Tải ($ scope.userTemplate) sạch hơn và không phá vỡ mô hình góc mã.
Tiago Roldão

1
Không - chỉ thị ng-view khá hạn chế - hay nói tốt hơn, nó dành riêng cho việc sử dụng với hệ thống định tuyến gốc (ngược lại, vẫn còn hạn chế, imho). Bạn có thể làm như đã nói, đưa nội dung vào một phần tử DOM, rồi gọi phương thức biên dịch $, để yêu cầu angle đọc lại cấu trúc. Điều đó sẽ kích hoạt bất kỳ ràng buộc nào cần được thực hiện.
Tiago Roldão

2
Hoạt động, nhưng bạn không nên thực hiện thao tác DOM trong bộ điều khiển của mình.
dmackerman

16

Tôi nghĩ cách dễ nhất để làm điều đó là giải quyết các tuyến đường sau đó, bạn có thể hỏi các tuyến đường qua json chẳng hạn. Kiểm tra xem tôi tạo một nhà máy sản xuất từ ​​$ routeProvider trong giai đoạn cấu hình, thông qua $ cung cấp, vì vậy tôi có thể tiếp tục sử dụng đối tượng $ routeProvider trong giai đoạn chạy và thậm chí trong bộ điều khiển.

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

Bí danh $routeProviderđể sử dụng như một factory(có sẵn sau config) sẽ không hoạt động tốt với tính bảo mật và tính liên kết của ứng dụng - theo cách nào đó, kỹ thuật tuyệt vời (ủng hộ!).
Cody

@Cody, bạn có thể giải thích lưu ý về bảo mật / liên kết ứng dụng không? Chúng ta đang ở trong một chiếc thuyền tương tự và một cái gì đó giống như câu trả lời ở trên sẽ thực sự hữu ích.
virtualandy

2
@virtualandy, nói chung đây không phải là một cách tiếp cận tuyệt vời vì bạn đang cung cấp một nhà cung cấp ở các giai đoạn khác nhau - do đó có thể làm rò rỉ các utils cấp cao cần được che giấu trong giai đoạn cấu hình. Đề xuất của tôi là sử dụng UI-Router (giải quyết rất nhiều khó khăn), sử dụng "giải quyết", "controllerAs" và các phần khác như thiết lập templateUrl thành một hàm. Tôi cũng đã xây dựng một mô-đun "presolve" mà tôi có thể chia sẻ có thể $ nội suy bất kỳ phần nào của lược đồ định tuyến (mẫu, templateUrl, bộ điều khiển, v.v.). Giải pháp trên là một giải pháp thanh lịch hơn - nhưng mối quan tâm chính của bạn là gì?
Cody

1
@virtualandy, đây là một mô-đun cho các mẫu, bộ điều khiển, v.v ... của lazyLoaded - xin lỗi nó không có trong repo, nhưng đây là một fiddle: jsfiddle.net/cCarlson/zdm6gpnb ... lazyLoads ở trên - VÀ - có thể nội suy bất cứ thứ gì dựa trên các tham số URL. Mô-đun có thể sử dụng một số công việc, nhưng ít nhất nó là một điểm khởi đầu.
Cody

@Cody - không vấn đề gì, cảm ơn vì mẫu và giải thích thêm. Makese cảm giác. Trong trường hợp của chúng tôi, chúng tôi đang sử dụng phân đoạn góc-tuyến và nó hoạt động tốt. Nhưng chúng tôi hiện cần hỗ trợ các tuyến đường "động" (một số triển khai có thể không có đầy đủ các tính năng / tuyến đường của trang hoặc có thể thay đổi tên của chúng, v.v.) và khái niệm tải trong một số JSON xác định các tuyến đường trước khi thực sự triển khai là suy nghĩ của chúng tôi nhưng lại gặp sự cố khi sử dụng các nhà cung cấp $ http / $ bên trong .config () rõ ràng.
virtualandy

7

Trong trình duyệt $ routeProvider URI, bạn có thể chỉ định các tham số biến, như sau $routeProvider.when('/page/:pageNumber' ...:, và truy cập nó trong bộ điều khiển của bạn thông qua $ routeParams.

Có một ví dụ điển hình ở cuối trang $ route: http://docs.angularjs.org/api/ng.$route

CHỈNH SỬA (cho câu hỏi đã chỉnh sửa):

Rất tiếc, hệ thống định tuyến rất hạn chế - có rất nhiều cuộc thảo luận về chủ đề này và một số giải pháp đã được đề xuất, cụ thể là bằng cách tạo nhiều chế độ xem được đặt tên, v.v. Nhưng hiện tại, lệnh ngView chỉ phục vụ MỘT chế độ xem trên mỗi tuyến đường, trên cơ sở một đối một. Bạn có thể thực hiện điều này theo nhiều cách - cách đơn giản hơn là sử dụng mẫu của chế độ xem làm trình tải, với một <ng-include src="myTemplateUrl"></ng-include>thẻ trong đó ($ scope.myTemplateUrl sẽ được tạo trong bộ điều khiển).

Tôi sử dụng một giải pháp phức tạp hơn (nhưng sạch hơn, cho các vấn đề lớn hơn và phức tạp hơn), về cơ bản bỏ qua dịch vụ $ route hoàn toàn, được nêu chi tiết ở đây:

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


Tôi thích ng-includephương pháp này. Nó thực sự đơn giản và đây là một cách tiếp cận tốt hơn nhiều so với thao tác DOM.
Neel

5

Không chắc tại sao điều này hoạt động nhưng các tuyến đường động (hoặc ký tự đại diện nếu bạn thích) có thể có trong góc 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 -> tải một phần "foo"

example.com/bar-> tải một phần "bar"

Không cần bất kỳ điều chỉnh nào trong ng-view. Trường hợp '/: a' là biến duy nhất mà tôi phát hiện ra sẽ làm nhức nhối điều này .. '/: foo' không hoạt động trừ khi các phần của bạn đều là foo1, foo2, v.v. ... '/: a' hoạt động với bất kỳ phần nào Tên.

Tất cả các giá trị đều kích hoạt bộ điều khiển động - vì vậy không có "khác" nhưng, tôi nghĩ đó là những gì bạn đang tìm kiếm trong kịch bản định tuyến động hoặc ký tự đại diện ..


2

Kể từ AngularJS 1.1.3, bây giờ bạn có thể thực hiện chính xác những gì bạn muốn bằng cách sử dụng tham số catch-all mới.

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

Từ cam kết:

Điều này cho phép routeProvider chấp nhận các tham số khớp với các chuỗi con ngay cả khi chúng chứa dấu gạch chéo nếu chúng được bắt đầu bằng dấu hoa thị thay vì dấu hai chấm. Ví dụ, các tuyến đường như thế edit/color/:color/largecode/*largecode này sẽ khớp với một cái gì đó như thế này http://appdomain.com/edit/color/brown/largecode/code/with/slashs.

Tôi đã tự mình thử nghiệm nó (sử dụng 1.1.5) và nó hoạt động rất tốt. Chỉ cần lưu ý rằng mỗi URL mới sẽ tải lại bộ điều khiển của bạn, vì vậy để giữ bất kỳ loại trạng thái nào, bạn có thể cần sử dụng dịch vụ tùy chỉnh.


0

Đây là một giải pháp khác hoạt động tốt.

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

    }

})();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.