angular-ui-router с requirejs, ленивая загрузка контроллера

Не могли бы вы помочь мне понять, как загрузить контроллер в приведенном ниже примере перед представлением? Похоже, что представление загружается сразу, а контроллер еще не загружен.

//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>

2 ответа

Я создал рабочий плункер здесь.

Давайте иметь этот 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>

Давайте соблюдать main.js - конфиг RequireJS:

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

На самом деле, мы создаем только псевдонимы (пути) для наших ControllerNames - и их Controller_Scripts.js файлы. Вот и все. Кроме того, мы возвращаемся, чтобы запросить приложение, но позже мы будем использовать другую функцию - для регистрации лениво загруженных контроллеров.

что это deps: ['app'] имею в виду? Во-первых, нам нужно предоставить файл app.js ("приложение" означает найти app.js ):

define([], function() {

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

это возвращаемое значение является тем, которое мы можем запросить в каждом загруженном асинхронном файле

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

Как мы будем лениво загружать контроллеры? Как уже доказано здесь для ngRoute

AngularAMD v0.2.1

angularAMD - это утилита, которая облегчает использование RequireJS в приложениях AngularJS, поддерживающих загрузку по требованию сторонних модулей, таких как angular-ui.

Мы будем просить угловой для ссылки на $controllerProvider - и использовать его позже, чтобы зарегистрировать контроллеры.

Это первая часть нашего 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;
  }
]);

Как мы видим, мы только что создали приложение 'app', а также создали держатель app_cached_providers (следуя стилю angularAMD). На этапе конфигурации мы просим угловой для $controllerProvider и сохранить ссылку на это.

Теперь давайте продолжим в 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;
          }],
        },
      });

  }
]);

Эта часть выше показывает декларацию двух государств. Один из них - 'home' стандартный, ленивый Это контроллер неявный, но стандарт может быть использован.

Второй штат называется "other" которая нацелена на безымянный вид ui-view="", И здесь мы видим, во-первых, ленивый груз. Внутри решимости (см.:)

Разрешить

Вы можете использовать решимость, чтобы предоставить вашему контроллеру контент или данные, настроенные для данного состояния. resolv - это необязательная карта зависимостей, которая должна быть введена в контроллер.

Если какие-либо из этих зависимостей являются обещаниями, они будут разрешены и преобразованы в значение до того, как будет создан экземпляр контроллера и будет вызвано событие $ stateChangeSuccess.

С этим в нашем наборе мы знаем, что контроллер (по его имени) будет найден в угловом репозитории, как только будет завершено разрешение:

// 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;
  }],
},

Хорошо, теперь, как упоминалось выше, основной содержит этот псевдоним def

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

А это значит, что файл "Controller_Other.js" будет найден и загружен. Это его содержание, которое делает магию. Наиболее важным здесь является использование ранее кэшированной ссылки на $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";
    });        
});

хитрость не в том, чтобы использовать app.controller() но

$controllerProvider.Register

Служба $ controller используется Angular для создания новых контроллеров. Этот провайдер разрешает регистрацию контроллера через register() метод.

Наконец, есть другое определение состояния с более узким разрешением... попытка сделать его более читабельным:

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

Выше мы видим, что мы разрешаем два контроллера для обоих / всех именованных представлений этого состояния.

Вот и все. Каждый контроллер, определенный здесь

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

будет загружен через разрешение и $controllerProvider - через RequireJS - лениво. Проверьте, что все здесь

Аналогичные вопросы и ответы: AngularAMD + ui-router + имя динамического контроллера?

В одном проекте я использовал ленивую загрузку контроллеров и должен был вручную вызвать $digest для области видимости, чтобы он работал. Я думаю, что это поведение не меняется с UI-маршрутизатором. Ты пробовал это?

define(['app'], function (app) {
  app.controller('TopMenuCtrl', ['$scope', function ($scope) {
    $scope.message = "It works";
    $scope.$digest();
  }]);
});
Другие вопросы по тегам