如何根据使用$ routeProvider注册的路径中的参数动态加载模板?

时间:2013-06-17 21:42:14

标签: angularjs angular-routing

我发现你可以使用template或templateUrl作为这样的函数

.when('/:controller/:action',{
   templateUrl:function(params){
     return: '/'+params.controller+'/'+params.action
  }
})

然后我想知道是否可能延迟加载模板,但我无法做到这一点

.when('/:ctrl/:action',{
   template:function(params){
      injector = angular.injector(['ng']);
      $q = injector.get('$q');
      var dfrd = $q.defer();

      // fetch template from server
      // dfrd.resolve()

      return dfrd.promise;      
   }
});

请告诉我有一种方法 - 我想通过向服务器发出ajax请求来获取整个模板

5 个答案:

答案 0 :(得分:19)

所以,我看到你是一名.NET MVC开发人员......

让我们看一下动作在.NET MVC中的作用:它们返回ActionResults ...它们真正体现了应该从架构中运行和返回的视图和模型。 MVC中的“控制器”是此类操作的集合,可能包含私有方法形式的那些操作之间的某些共享逻辑或该类的共享依赖项。

现在让我们看一下Angular正在做什么:Angular正在调用控制器函数一次,作为构造函数,通常设置一个$ scope对象,它将作为你的“模型”行事。 Angular中的每个控制器(通常都是)与一种且只有一种方式建立$ scope **相关联。处理完该控制器后,会在其更改的$ scope上调用$ digest,然后将其应用于绑定到范围的视图...这是由ng控制器元素封装的html(或ng-view)属性。

那么..问题,你能根据路由参数动态加载模板吗?是。你绝对可以。最简单的方法是将您的请求路由到一个模板,该模板除了包含ng-include的div之外什么都没有,您更改了。

在您的路线中:

$routeProvider.when('/:controller/:action', { 
    controller: 'DynamicCtrl', 
    template: '<div ng-include="include"></div>' 
});

然后你的动态控制器声明:

app.controller('DynamicCtrl', function($scope, $routeParams) {
    $scope.include = $routeParams.controller + '/' + $routeParams.action;
});

MyController / MyAction(我假设您可能使用Razor生成)应返回如下内容:

<div ng-controller="MyActionCtrl">
   do stuff!
</div>

...从那里你可以自己定义你的MyActionCtrl控制器。

你可以弯曲架构使其成为“ASP.Net MVC-esque”,因为“一个控制器”中有一大堆“动作”功能,它决定了你视图的整个行为?是的......但是,如果没有使用switch语句之类的应用程序真的很愚蠢......那么你可能不希望这样做。

最终,你将不得不放弃使用Angular的ASP.Net MVC的“控制器和动作”的思维模式。这是一个MVC架构,但是MVC!=“控制器&amp;&amp; actions”。


** Angular也会创建并保存控制器的实例作为对象,但是在Angular开发中很少使用该功能,而且我不是在这里谈论它。)

答案 1 :(得分:3)

正如所指出的,templateUrl加载的模板在正常情况下将是延迟加载的,如果你需要除了标准之外的东西,还有一些方法可以使用缓存。

这个评论似乎是问题的症结所在:

  

我想通过向服务器发出ajax请求来获取整个模板

这不一定是使用角度的常规方法。它可以有它的用处,但它已经走了很多路。通常模板不受上下文敏感的所有内容的影响,因为它们代表从$ scope或某种类型的参数获取其关键状态信息的代码。为什么ajax?

您的模板想要/需要什么来自ajax请求?这个问题的答案可能有助于最终决定如何在最合适的地方实现最佳效果。

您可能希望或需要一些服务器端动态作为系统的一部分,或者有其他方法可以做到这一点,而不是让ajax返回定义模板。

更新所以你说'在路由更改时我需要从服务器获取模板,使用$ http异步“。”但这些都是内置于templateUrl。@ wmluke似乎提出了一些有趣的选择。

如果我们接受您需要ajax来获取模板,那么这是另一种可能性。使用ui-router,您可以使用templateProvider - 它可以调用您自己的自定义服务来执行ajax以及您想要的任何其他内容。

此示例只是异步返回Hello world,但您可以在此处调用自己的服务或直接执行ajax请求。

templateProvider:
 [        '$timeout',
  function ($timeout) {
   return $timeout(function () { return "Hello world" }, 100);
 }],

购买ui-router是一项潜在的更大承诺,但它可能会产生奖励,而不仅仅是能够编写代码来生成模板。祝你好运!

答案 2 :(得分:2)

为什么不一起绕过templateURL并让控制器拦截请求?

  • 您可以通过网址动态延迟加载页面

  • 可选择拦截部分内容。

<强> config.js

$routeProvider.when('/contact', {
  template: '/views/contact.html'
});

<强> controllers.js

controller('AppController', ['$scope' function($scope) {    
  //Do whatever you want here
  $scope.$on("$routeChangeSuccess",function( $currentRoute, $previousRoute ){
    console.log($route.current.template);
    $scope.page = $route.current.template;
  });

  //Optionally intercept partials
  $scope.returnView = function(partial){
    console.log(partial);
    return partial;
  }  
}]).

<强>的index.html

<html lang="en" ng-app="myApp" ng-controller="AppController">
<body>
<ng-include src="returnView('/views/global.header.html')"></ng-include>
<ng-include src="page"></ng-include>
<ng-include src="returnView('/views/global.footer.html')"></ng-include>
</body>
</html>

答案 3 :(得分:1)

默认情况下,Angular模板是延迟加载的。具体而言,在需要模板之前不会提取模板,然后将模板缓存在$templateCache中。

但是,根据您的示例,我怀疑您正在寻找$routeProvider中路由定义的resolve属性。此属性允许您将promise依赖项传递给控制器​​。请参阅$route ...

中的延迟示例
$routeProvider.when('/Book/:bookId', {
    templateUrl: 'book.html',
    controller: BookCntl,
    resolve: {
        // I will cause a 1 second delay
        delay: function($q, $timeout) {
            var delay = $q.defer();
            $timeout(delay.resolve, 1000);
            return delay.promise;
        }
    }
});

此外,您可能有兴趣预加载一些模板但延迟加载其他模板。在这种情况下,你应该看看......

答案 4 :(得分:0)

您可以根据angular docs site ngInclude - Example Paragraph上的参数查看动态加载的两个模板的工作示例。

这是Plunker example

<!-- index.html -->
<select ng-model="template" ng-options="t.name for t in templates">
    <option value="">(blank)</option>
</select>
<div class="slide-animate" ng-include="template.url"></div>


// controller
$scope.templates = [ 
    { name: 'template1.html', url: 'template1.html'},
    { name: 'template2.html', url: 'template2.html'} 
];
$scope.template = $scope.templates[0];