【问题标题】:AngularJS dynamic routingAngularJS 动态路由
【发布时间】:2012-11-20 19:01:39
【问题描述】:

我目前有一个内置路由的 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 设置为正确的值。

然而我现在想用新的 templateUrl 值更改 ng-view。这是如何实现的?

【问题讨论】:

    标签: javascript angularjs content-management-system url-routing


    【解决方案1】:
    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/selling/list 将被此提供商捕获

    来自文档(1.3.0):

    "如果templateUrl是一个函数,它会被调用如下 参数:

    {Array.} - 从当前提取的路由参数 $location.path() 通过应用当前路由"

    还有

    何时(路径,路线):方法

    • 路径可以包含以冒号开头并以星号结尾的命名组:例如:名称*。当路由匹配时,所有字符都急切地存储在给定名称下的 $routeParams 中。

    【讨论】:

    • 需要与时俱进...我构建的 v1.0.2 中缺少的功能现在可用。更新已接受的答案
    • 老实说,我从来没有在当前的 1.3 测试版中使用过这个
    • 当我这样做时,它实际上已经工作了...... when('/:page', { templateUrl : function(parameters) { return parameters.page + '.html'; }
    • 说真的.. Angular 没有将它作为默认行为真的很愚蠢......手动路由很荒谬
    • 请解释一下,urlattr是从哪里设置或发送的,我的意思是urlattr.name会产生什么?
    【解决方案2】:

    好的,解决了。

    将解决方案添加到 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 中添加了 $apply 和 $compile,以便可以注入动态内容。

    【讨论】:

    • 这仅适用于静态内容,如果我理解正确的话。这是因为您在通过 jQuery(在 Angular 范围之外)实例化控制器后正在更改 DOM。像 {{this}} 这样的变量可能会起作用(我必须尝试一下),但指令很可能不会。请注意这一点,因为它现在可能很有用,但以后可能会破坏代码..
    • 没错,绑定不起作用,但对我来说,我并不太担心,因为我只希望客户能够添加新页面。我添加的任何页面我都会通过路由配置适当地指定。不过好点。
    • 如果要渲染静态 html 内容,这不是一个糟糕的解决方案。只要您牢记,您本质上就是用静态(非角度)内容覆盖 ng-view。考虑到这一点,将两者分开会更干净,创建类似
      元素,并在那里注入您的“用户模板”。此外,重写 $route.current.templateUrl 绝对没有意义 - 创建一个 $scope.userTemplate 模型并执行 $('#user-views").load($scope.userTemplate) 更干净,并且不会破坏任何角度代码。
    • 不 - ng-view 指令相当有限 - 或者更好地说,它专门用于本机路由系统(反过来,这仍然是有限的,恕我直言)。你可以照你说的做,将内容注入到 DOM 元素中,然后调用 $compile 方法,告诉 angular 重新读取结构。这将触发任何需要完成的绑定。
    • 有效,但您不应该在控制器中进行 DOM 操作。
    【解决方案3】:

    我认为最简单的方法是稍后解析路由,例如,您可以通过 json 查询路由。看看我在配置阶段通过 $provide 从 $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,这通常不是一个好方法,因为您要让提供程序在不同阶段可用——这反过来可能会泄漏应该在配置阶段隐藏的高级实用程序。我的建议是使用 UI-Router(解决了很多困难),使用“resolve”、“controllerAs”和其他功能,例如将 templateUrl 设置为函数。我还构建了一个“presolve”模块,我可以共享它可以 $interpolate 路由方案的任何部分(模板、templateUrl、控制器等)。上面的解决方案是一个更优雅的解决方案——但您主要关心的是什么?
    • @virtualandy,这是一个用于lazyLoaded 模板、控制器等的模块——很抱歉它不在回购中,但这里有一个小提琴:jsfiddle.net/cCarlson/zdm6gpnb ... 这个lazyLoads 上面的内容——和—— - 可以根据 URL 参数插入任何内容。该模块可以使用一些工作,但它至少是一个起点。
    • @Cody - 没问题,感谢您的示例和进一步的解释。有道理。在我们的例子中,我们使用angular-route-segment,它运行良好。但是我们现在需要支持“动态”路由(某些部署可能没有页面的完整功能/路由,或者可能更改它们的名称等),并且在实际实现之前加载一些定义路由的 JSON 的概念是我们的想法但显然在 .config() 中使用 $http/$providers 时遇到了问题。
    【解决方案4】:

    在 $routeProvider URI 模式中,您可以指定可变参数,例如:$routeProvider.when('/page/:pageNumber' ...,并通过 $routeParams 在控制器中访问它。

    $route 页面末尾有一个很好的例子: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-view 不需要做任何调整。 '/:a' 案例是我发现的唯一可以实现这一点的变量。'/:foo' 不起作用,除非你的部分都是 foo1、foo2 等......'/:a' 适用于任何部分名字。

    所有值都会触发动态控制器 - 因此没有“否则”,但我认为这是您在动态或通配符路由场景中寻找的内容..

    【讨论】:

      【解决方案6】:

      从 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 都会重新加载您的控制器,因此要保持任何类型的状态,您可能需要使用自定义服务。

      【讨论】:

        【解决方案7】:

        这是另一种效果很好的解决方案。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-09
          • 1970-01-01
          • 2015-05-15
          • 1970-01-01
          • 2016-08-31
          • 2016-03-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多