การกำหนดเส้นทางแบบไดนามิก 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', ตัวควบคุม: 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 () ปัจจุบันโดยใช้เส้นทางปัจจุบัน "

นอกจากนี้

เมื่อ (เส้นทางเส้นทาง): วิธีการ

  • เส้นทางสามารถมีกลุ่มที่ตั้งชื่อโดยเริ่มต้นด้วยเครื่องหมายจุดคู่และลงท้ายด้วยดาว: เช่นชื่อ * อักขระทั้งหมดจะถูกจัดเก็บอย่างกระตือรือร้นใน $ routeParams ภายใต้ชื่อที่กำหนดเมื่อเส้นทางตรงกัน

5
จำเป็นต้องย้ายไปตามเวลา ... ฟังก์ชันที่ฉันสร้างขึ้นซึ่งขาดหายไปใน v1.0.2 พร้อมใช้งานแล้ว การอัปเดตคำตอบที่ยอมรับสำหรับคำถามนี้
Greg

บอกตามตรงว่าฉันไม่เคยทำงานนี้กับ 1.3 betas ปัจจุบัน
Archimedes Trajano

ใช้งานได้จริงเมื่อฉันทำสิ่งนี้ ... เมื่อ ('/: page', {templateUrl: function (parameters) {return parameters.page + '.html';}
Archimedes Trajano

อย่างจริงจัง .. มันโง่จริงๆที่ Angular ไม่มีมันเป็นพฤติกรรมเริ่มต้น ... การกำหนดเส้นทางด้วยตนเองนั้นไร้สาระ
Guilherme Ferreira

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 เรียกว่า portfolio.html เปลี่ยนเนื้อหาบางส่วนและป้อน/#/pages/portfolioลงในเบราว์เซอร์ของฉันและเดี๋ยวก่อน presto portfolio.html ก็ปรากฏขึ้น ....

อัปเดต เพิ่มแล้ว $ ใช้และ $ คอมไพล์เป็น html เพื่อให้สามารถแทรกเนื้อหาแบบไดนามิกได้


2
สิ่งนี้ใช้ได้กับเนื้อหาคงที่เท่านั้นถ้าฉันเข้าใจถูกต้อง เนื่องจากคุณกำลังแก้ไข DOM หลังจากที่คุณสร้างอินสแตนซ์คอนโทรลเลอร์ผ่าน jQuery (นอกขอบเขตของเชิงมุม) ตัวแปรเช่น {{this}} อาจใช้งานได้ (ฉันจะต้องลองดู) แต่คำสั่งมักจะไม่เป็นเช่นนั้น โปรดระวังสิ่งนี้เนื่องจากอาจมีประโยชน์ในขณะนี้ แต่สามารถแบ่งรหัสได้ในภายหลัง ..
Tiago Roldão

จริงอยู่การผูกไม่ได้ผลสำหรับฉัน แต่ฉันก็ไม่ได้งอแงมากนักเพราะฉันต้องการให้ลูกค้าสามารถเพิ่มเพจใหม่ได้เท่านั้น หน้าใด ๆ ที่ฉันเพิ่มฉันจะระบุอย่างเหมาะสมผ่านการกำหนดค่าเส้นทาง จุดที่ดีแม้ว่า
Greg

1
ไม่ใช่วิธีแก้ปัญหาที่ไม่ดีหากคุณต้องการแสดงเนื้อหา html แบบคงที่ ตราบใดที่คุณจำไว้ว่าคุณกำลังลบล้าง ng-view ด้วยเนื้อหาคงที่ (ไม่ใช่เชิงมุม) ด้วยเหตุนี้การแยกทั้งสองอย่างจะดีกว่าโดยสร้างองค์ประกอบเช่น <div id = "user-views"> </div> และฉีด "เทมเพลตผู้ใช้" ที่นั่น นอกจากนี้ไม่มีเหตุผลอย่างยิ่งในการแทนที่ $ route.current.templateUrl - การสร้างแบบจำลอง $ scope.userTemplate และการทำ $ ('# user-views ") load ($ scope.userTemplate) นั้นสะอาดกว่าและไม่มีการหักมุมใด ๆ รหัส
Tiago Roldão

1
ไม่ - คำสั่ง ng-view ค่อนข้าง จำกัด - หรือพูดให้ดีกว่านั้นคือเฉพาะสำหรับใช้กับระบบการกำหนดเส้นทางดั้งเดิม (ซึ่งในทางกลับกันก็ยัง จำกัด imho) คุณสามารถทำตามที่คุณกล่าวไว้โดยฉีดเนื้อหาลงในองค์ประกอบ DOM จากนั้นเรียกวิธีการคอมไพล์ $ เพื่อบอกให้เชิงมุมอ่านโครงสร้างซ้ำ ซึ่งจะทำให้เกิดการผูกที่ต้องทำ
Tiago Roldão

2
ใช้งานได้ แต่คุณไม่ควรทำการจัดการ DOM ในคอนโทรลเลอร์ของคุณ
dmackerman

16

ฉันคิดว่าวิธีที่ง่ายที่สุดในการทำสิ่งนั้นคือการแก้ไขเส้นทางในภายหลังคุณสามารถถามเส้นทางผ่าน json ได้เช่น ตรวจสอบว่าฉันสร้างโรงงานจาก $ routeProvider ในระหว่างขั้นตอนการกำหนดค่าผ่านทาง $ ให้ดังนั้นฉันจึงสามารถใช้ออบเจ็กต์ $ 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 (ดูแลความยากลำบากมากมาย) ใช้ "แก้ไข" "controllerAs" และคณะอื่น ๆ เช่นการตั้งค่า templateUrl ให้กับฟังก์ชัน ฉันยังสร้างโมดูล "presolve" ที่ฉันสามารถแบ่งปันซึ่งสามารถ $ สอดแทรกส่วนใดส่วนหนึ่งของโครงร่างเส้นทาง (เทมเพลต, templateUrl, คอนโทรลเลอร์ ฯลฯ ) วิธีแก้ปัญหาข้างต้นเป็นวิธีที่หรูหรากว่า - แต่คุณกังวลอะไรที่สำคัญ?
Cody

1
@virtualandy นี่คือโมดูลสำหรับเทมเพลต lazyLoaded คอนโทรลเลอร์และอื่น ๆ - ขออภัยที่ไม่ได้อยู่ใน repo แต่นี่คือซอ: jsfiddle.net/cCarlson/zdm6gpnb ... lazyLoads ด้านบนนี้ - และ - สามารถแก้ไขอะไรก็ได้ ขึ้นอยู่กับพารามิเตอร์ URL โมดูลสามารถใช้งานได้ แต่อย่างน้อยก็เป็นจุดเริ่มต้น
Cody

@Cody - ไม่มีปัญหาขอบคุณสำหรับตัวอย่างและคำอธิบายเพิ่มเติม ทำให้รู้สึก ในกรณีของเราเรากำลังใช้angular-route-segmentและใช้งานได้ดี แต่ตอนนี้เราจำเป็นต้องสนับสนุนเส้นทาง "ไดนามิก" (บาง Deploys อาจไม่มีคุณลักษณะ / เส้นทางทั้งหมดของเพจหรืออาจเปลี่ยนชื่อเป็นต้น) และแนวคิดของการโหลดใน JSON บางตัวที่กำหนดเส้นทางก่อนที่จะนำไปใช้จริงเป็นความคิดของเรา แต่พบปัญหาในการใช้ผู้ให้บริการ $ http / $ ภายใน .config () อย่างเห็นได้ชัด
virtualandy

7

ใน $ routeProvider URI patters คุณสามารถระบุพารามิเตอร์ตัวแปรเช่นนี้: $routeProvider.when('/page/:pageNumber' ...และเข้าถึงในคอนโทรลเลอร์ของคุณผ่าน $ routeParams

มีตัวอย่างที่ดีที่ท้ายหน้า $ route: http://docs.angularjs.org/api/ng.$route

แก้ไข (สำหรับคำถามที่แก้ไข):

ระบบการกำหนดเส้นทางมีข้อ จำกัด อย่างมาก - มีการอภิปรายมากมายในหัวข้อนี้และมีการเสนอวิธีแก้ปัญหาบางอย่าง ได้แก่ การสร้างมุมมองที่มีชื่อหลาย ๆ มุมมองเป็นต้น แต่ตอนนี้คำสั่ง ngView ให้บริการเพียงหนึ่งมุมมองต่อเส้นทางบน แบบตัวต่อตัว คุณสามารถทำสิ่งนี้ได้หลายวิธีวิธีที่ง่ายกว่าคือการใช้เทมเพลตของมุมมองเป็นตัวโหลดโดยมี<ng-include src="myTemplateUrl"></ng-include>แท็กอยู่ ($ scope.myTemplateUrl จะถูกสร้างขึ้นในคอนโทรลเลอร์)

ฉันใช้วิธีแก้ปัญหาที่ซับซ้อนกว่า (แต่สะอาดกว่าสำหรับปัญหาที่ใหญ่กว่าและซับซ้อนกว่า) โดยทั่วไปจะข้ามบริการเส้นทาง $ ไปทั้งหมดซึ่งมีรายละเอียดอยู่ที่นี่:

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


ฉันชอบng-includeวิธีการนี้ มันง่ายมากและนี่เป็นแนวทางที่ดีกว่าการจัดการ DOM
Neel

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-view กรณี '/: a' เป็นตัวแปรเดียวที่ฉันพบว่าจะบรรลุสิ่งนี้ .. '/: foo' ไม่ทำงานเว้นแต่ว่า partials ของคุณจะเป็น foo1, foo2 ฯลฯ ทั้งหมด ... '/: a' ทำงานร่วมกับบางส่วน ชื่อ.

ค่าทั้งหมดจะเริ่มการทำงานของตัวควบคุมแบบไดนามิกดังนั้นจึงไม่มี "อย่างอื่น" แต่ฉันคิดว่านี่คือสิ่งที่คุณกำลังมองหาในสถานการณ์การกำหนดเส้นทางแบบไดนามิกหรือสัญลักษณ์แทน ..


2

สำหรับ AngularJS 1.1.3 ตอนนี้คุณสามารถทำสิ่งที่คุณต้องการโดยใช้พารามิเตอร์ catch-all ใหม่

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;

    }

})();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.