带有requirejs的angular-ui-router,控制器的延迟加载 [英] angular-ui-router with requirejs, lazy loading of controller

查看:24
本文介绍了带有requirejs的angular-ui-router,控制器的延迟加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你能帮我理解如何在下面的例子中在视图之前加载控制器吗?看起来好像是在控制器尚未加载时立即加载了视图.

//app.js$stateProvider.state('索引', {网址:/",意见:{顶部菜单": {templateUrl: "/Home/TopMenu",控制器:函数($scope,$injector){要求(['控制器/顶部菜单控制器'],功能(模块){$injector.invoke(module, this, { '$scope': $scope });});}}}});//顶部菜单控制器.js定义(['应用程序'],函数(应用程序){app.controller('TopMenuCtrl', ['$scope', function ($scope) {$scope.message = "它有效";}]);});//首页/顶部菜单<h3>TopMenu</h3><div ng-controller="TopMenuCtrl">{{信息}}

解决方案

创建了工作 plunker 这里.

让我们看看这个index.html:

<头><title>我的懒惰</title><body ng-app="app"><a href=#/home">#/home</a>//我们有三个状态——'home' 不是懒惰的<a href=#/">#/</a>- index//'index' 是惰性的,有两个视图<a href=#/other">#/other</a>//'other' 是懒惰的,带有未命名的视图<div data-ui-view="topMenu"></div><div data-ui-view=""></div><script src="angular.js"></script>//标准角度<script src="angular-ui-router.js"></script>//和 ui-router 脚本<script src="script.js"></script>//我们的应用<script data-main="main.js";//懒惰的依赖src=require.js"></script></html>

让我们观察 main.js - RequireJS 配置:

require.config({//baseUrl: "js/scripts",baseUrl: "",//别名库路径路径:{//这里我们定义了 NAMES 的路径//使控制器和它们的惰性文件名独立TopMenuCtrl":Controller_TopMenu",ContentCtrl":Controller_Content",其他控件":控制器_其他",},deps: ['应用程序']});

事实上,我们只为我们的 ControllerNames 和它们的 Controller_Scripts.js 文件创建别名(路径).就是这样.此外,我们返回到 require 应用程序,但在我们的示例中稍后将使用不同的功能 - 注册延迟加载的控制器.

deps: ['app'] 是什么意思?首先,我们需要提供文件 app.js ('app' 表示查找 app.js) :

define([], function() {var app = angular.module('app');返回应用程序;})

这个返回值是我们可以在每个异步加载的文件中要求的值

define(['app'], function (app) {//在这里我们可以访问模块(app")});

我们将如何延迟加载控制器?正如这里已经证明的 ngRoute

angularAMD v0.2.1

<块引用>

angularAMD 是一种实用程序,可促进在 AngularJS 应用程序中使用 RequireJS,支持按需加载 angular-ui 等 3rd 方模块.

我们将要求 angular 提供对 $controllerProvider - 稍后使用它来注册控制器.

这是我们 script.js 的第一部分:

//I.应用var app = angular.module('app', [ui.router"]);//二.缓存 $controllerProvidervar app_cached_providers = {};app.config(['$controllerProvider',功能(控制器提供者){app_cached_providers.$controllerProvider = controllerProvider;}]);

如我们所见,我们刚刚创建了应用程序app",还创建了持有者 app_cached_providers (遵循 angularAMD 风格).在配置阶段,我们向 angular 请求 $controllerProvider 并保留参考.

现在让我们在 script.js 中继续:

//III.内联依赖表达式app.config(['$stateProvider', '$urlRouterProvider',功能($stateProvider,$urlRouterProvider){$urlRouterProvider.否则(/家");$stateProvider.state(家",{网址:/家",模板:<div>这是家 - 没有延迟加载</div>"});$stateProvider.state(其他",{网址:/其他",模板:<div>来自 ctrl 的消息:{{message}}</div>",控制器:OtherCtrl",解决: {loadOtherCtrl: ["$q", function($q) {var deferred = $q.defer();require([OtherCtrl"], function() { deferred.resolve(); });返回 deferred.promise;}],},});}]);

上面的这部分显示了两种状态声明.其中之一 - 'home' 是标准的非懒惰的.它的控制器是隐式的,但可以使用标准.

第二个是名为other"的状态,它的目标是未命名的视图ui-view=".在这里我们可以首先看到,延迟负载.resolve (见:)

的内部

解决

<块引用>

您可以使用 resolve 为您的控制器提供针对状态自定义的内容或数据.resolve 是一个可选的依赖映射,应该被注入到控制器中.

如果这些依赖项中的任何一个是 promises,它们将在控制器被实例化之前被解析并转换为一个值并触发 $stateChangeSuccess 事件.

在我们的套件中,我们知道,一旦解析完成,将在 angular 存储库中搜索控制器(按其名称):

//将搜索此控制器名称 - 仅在解析完成后控制器:OtherCtrl",//让我们问问 RequireJS解决: {loadOtherCtrl: ["$q", function($q) {//我们需要 $q 等待var deferred = $q.defer();//并使其解决,一旦需要将加载文件require([OtherCtrl"], function() { deferred.resolve(); });返回 deferred.promise;}],},

好的,现在,如上所述,main 包含这个别名 def

//别名库路径路径:{...其他控件":控制器_其他",

这意味着文件Controller_Other.js"将被搜索和加载.这是其魔法的内容.这里最重要的是使用先前缓存的对 $controllerProvider

//Controller_Other.js"的内容;定义(['应用程序'],函数(应用程序){//默认控制器//被添加到app"模块中//懒惰,而且只有一次app_cached_providers.$controllerProvider.register('OtherCtrl', 函数 ($scope) {$scope.message = "OtherCtrl";});});

诀窍不是使用app.controller()而是

$controllerProvider.Register

<块引用>

Angular 使用 $controller 服务来创建新的控制器.此提供程序允许通过 register() 方法注册控制器.

最后还有另一个状态定义,解析范围更窄……尝试使其更具可读性:

//IV ... 使用辅助函数构建对象//然后分配给状态提供者var loadController = 函数(控制器名称){返回[$q",函数($q){var deferred = $q.defer();要求([控制器名称],函数(){deferred.resolve();});返回 deferred.promise;}];}app.config(['$stateProvider', '$urlRouterProvider',功能($stateProvider,$urlRouterProvider){变量索引 = {网址:/",意见:{顶部菜单":{模板:<div>来自 ctrl 的消息:{{message}}</div>",控制器:TopMenuCtrl",},":{模板:<div>来自 ctrl 的消息:{{message}}</div>",控制器:ContentCtrl",},},解决 : { },};index.resolve.loadTopMenuCtrl = loadController(TopMenuCtrl");index.resolve.loadContentCtrl = loadController(ContentCtrl");$stateProvider.state(索引",索引);}]);

上面我们可以看到,我们为该状态的两个/所有命名视图解析了两个控制器

就是这样.这里定义的每个控制器

路径:{TopMenuCtrl":Controller_TopMenu",ContentCtrl":Controller_Content",其他控件":控制器_其他",...},

将通过 resolve 和 $controllerProvider - 通过 RequireJS - 延迟加载.检查所有此处

类似问答A:AngularAMD + ui-router + 动态控制器名称?

Could you help me to understand how to load controller in the example below before the view? It looks like the view is loaded just immediately while the controller is not loaded yet.

//app.js
$stateProvider.state('index', {
    url: "/",
    views: {
        "topMenu": {
            templateUrl: "/Home/TopMenu",
            controller: function($scope, $injector) {
                require(['controllers/top-menu-controller'], function(module) {
                    $injector.invoke(module, this, { '$scope': $scope });
                });
            }
        }
    }
});

//top-menu-controller.js
define(['app'], function (app) {
    app.controller('TopMenuCtrl', ['$scope', function ($scope) {
        $scope.message = "It works";
    }]);
});

//Home/TopMenu
<h3>TopMenu</h3>
<div ng-controller="TopMenuCtrl">
    {{message}}
</div>

解决方案

I created working plunker here.

Let's have this index.html:

<!DOCTYPE html>
<html>
  <head>
    <title>my lazy</title>    
  </head>

  <body ng-app="app">
    
      <a href="#/home">#/home</a>     // we have three states - 'home' is NOT lazy
      <a href="#/">#/</a>  - index    // 'index' is lazy, with two views
      <a href="#/other">#/other</a>   // 'other' is lazy with unnamed view
    
    <div data-ui-view="topMenu"></div>        
    <div data-ui-view=""></div>
    
    <script src="angular.js"></script>           // standard angular
    <script src="angular-ui-router.js"></script> // and ui-router scritps

    <script src="script.js"></script>            // our application

    <script data-main="main.js"                  // lazy dependencies
        src="require.js"></script>
     
  </body>    
</html>

Let's observe the main.js - the RequireJS config:

require.config({

    //baseUrl: "js/scripts",
    baseUrl: "",

    // alias libraries paths
    paths: { 
      
        // here we define path to NAMES
        // to make controllers and their lazy-file-names independent
        
        "TopMenuCtrl": "Controller_TopMenu",
        "ContentCtrl": "Controller_Content",
        "OtherCtrl"  : "Controller_Other",
    },

    deps: ['app']
});

In fact, we only create aliases (paths) for our ControllerNames - and their Controller_Scripts.js files. That's it. Also, we return to require the app, but we will in our case use different feature later - to register lazily loaded controllers.

what does the deps: ['app'] mean? Firstly, we need to provide file app.js (the 'app' means find app.js) :

define([], function() {

  var app = angular.module('app');
  return app; 
})

this returned value is the one we can ask for in every async loaded file

define(['app'], function (app) {
    // here we would have access to the module("app")
});

How will we load controllers lazily? As already proven here for ngRoute

angularAMD v0.2.1

angularAMD is an utility that facilitates the use of RequireJS in AngularJS applications supporting on-demand loading of 3rd party modules such as angular-ui.

We will ask angular for a reference to $controllerProvider - and use it later, to register controllers.

This is the first part of our script.js:

// I. the application
var app = angular.module('app', [
  "ui.router"
]);


// II. cached $controllerProvider
var app_cached_providers = {};

app.config(['$controllerProvider',
  function(controllerProvider) {
    app_cached_providers.$controllerProvider = controllerProvider;
  }
]);

As we can see, we just created the application 'app' and also, created holder app_cached_providers (following the angularAMD style). In the config phase, we ask angular for $controllerProvider and keep reference for it.

Now let's continue in script.js:

// III. inline dependency expression
app.config(['$stateProvider', '$urlRouterProvider',
  function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider
      .otherwise("/home");

    $stateProvider
      .state("home", {
        url: "/home",
        template: "<div>this is home - not lazily loaded</div>"
      });

    $stateProvider
      .state("other", {
        url: "/other",
        template: "<div>The message from ctrl: {{message}}</div>",
        controller: "OtherCtrl",
        resolve: {
          loadOtherCtrl: ["$q", function($q) { 
            var deferred = $q.defer();
            require(["OtherCtrl"], function() { deferred.resolve(); });
            return deferred.promise;
          }],
        },
      });

  }
]);

This part above shows two states declaration. One of them - 'home' is standard none lazy one. It's controller is implicit, but standard could be used.

The second is state named "other" which does target unnamed view ui-view="". And here we can firstly see, the lazy load. Inside of the resolve (see:)

Resolve

You can use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.

If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.

With that in our suite, we know, that the controller (by its name) will be searched in angular repository once the resolve is finished:

// this controller name will be searched - only once the resolve is finished
controller: "OtherCtrl",
// let's ask RequireJS
resolve: {
  loadOtherCtrl: ["$q", function($q) { 
    // wee need $q to wait
    var deferred = $q.defer();
    // and make it resolved once require will load the file
    require(["OtherCtrl"], function() { deferred.resolve(); });
    return deferred.promise;
  }],
},

Good, now, as mentioned above, the main contains this alias def

// alias libraries paths
paths: {       
    ...
    "OtherCtrl"  : "Controller_Other",

And that means, that the file "Controller_Other.js" will be searched and loaded. This is its content which does the magic. The most important here is use of previously cached reference to $controllerProvider

// content of the "Controller_Other.js"

define(['app'], function (app) {
    // the Default Controller
    // is added into the 'app' module
    // lazily, and only once
    app_cached_providers
      .$controllerProvider
      .register('OtherCtrl', function ($scope) {
        $scope.message = "OtherCtrl";
    });        
});

the trick is not to use app.controller() but

$controllerProvider.Register

The $controller service is used by Angular to create new controllers. This provider allows controller registration via the register() method.

Finally there is another state definition, with more narrowed resolve... a try to make it more readable:

// IV ... build the object with helper functions
//        then assign to state provider    
var loadController = function(controllerName) {
  return ["$q", function($q) {
      var deferred = $q.defer();
      require([controllerName], function() {deferred.resolve(); });
      return deferred.promise;
  }];
}    

app.config(['$stateProvider', '$urlRouterProvider',
  function($stateProvider, $urlRouterProvider) {

    var index = {
        url: "/",
        views: {
          "topMenu": {
            template: "<div>The message from ctrl: {{message}}</div>",
            controller: "TopMenuCtrl",
          },
          "": {
            template: "<div>The message from ctrl: {{message}}</div>",
            controller: "ContentCtrl",
          },
        },
        resolve : { },
    };        
    index.resolve.loadTopMenuCtrl = loadController("TopMenuCtrl");
    index.resolve.loadContentCtrl = loadController("ContentCtrl");
    
    $stateProvider
      .state("index", index);          
}]);

Above we can see, that we resolve two controllers for both/all named views of that state

That's it. Each controller defined here

paths: { 
    "TopMenuCtrl": "Controller_TopMenu",
    "ContentCtrl": "Controller_Content",
    "OtherCtrl"  : "Controller_Other",
    ...
},

will be loaded via resolve and $controllerProvider - via RequireJS - lazily. Check that all here

Similar Q & A: AngularAMD + ui-router + dynamic controller name?

这篇关于带有requirejs的angular-ui-router,控制器的延迟加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆