この単純化されたシナリオでは、index.htm、lazy.htmの2つのファイルがあります。
index.htm:
var myApp = angular.module('myApp', []);
myApp.controller('embed',function($scope){
$scope.embed = 'Embedded Controller';
});
<div ng-controller="embed">{{embed}}</div>
<div ng-include="'lazy.htm'"></div>
lazy.htm
myApp.controller('lazy',function($scope){
$scope.lazy = 'Lazy Controller';
});
<div ng-controller="lazy">
{{lazy}}
</div>
結果はエラーです:「引数 'lazy'は関数ではなく、未定義になりました」
代わりに関数を使用する
lazy.htm
function lazy($scope) {
$scope.lazy = 'Lazy Controller';
}
<div ng-controller="lazy">
{{lazy}}
</div>
これは、バージョン1.3ベータ14まで機能します。ベータ15では、グローバルコントローラー機能が削除されました。 https://github.com/angular/angular.js/issues/8296
それでは、lazy.htmの角度付きコンテンツを動的に取得するより良い方法は何ですか?
UPDATE:
この記事( http://ify.io/lazy-loading-in-angularjs )では、別の解決策を見つけました。 $ controllerProviderを使用すると、angularブートストラップの後に新しいコントローラーを登録できます。チャームのように動作します。v1.3.0-beta.18
index.htm:
var myApp = angular.module('myApp', [])
.controller('embed',function($scope){
$scope.embed = 'Embedded Controller';
})
.config(function($controllerProvider) {
myApp.cp = $controllerProvider;
});
<div ng-controller="embed">{{embed}}</div>
<div ng-include="'lazy.htm'"></div>
lazy.htm
myApp.cp.register('lazy',function($scope){
$scope.lazy = 'Lazy Controller';
});
<div ng-controller="lazy">
{{lazy}}
</div>
更新2:
動作する他の2つの選択肢は次のとおりです。
lazy.htm
_app = $('[ng-app]').scope();
_app.lazy = function($scope) {
$scope.lazy = 'Lazy Controller';
};
OR
var $rootScope = $('[ng-app]').injector().get('$rootScope');
$rootScope.lazy = function($scope) {
$scope.lazy = 'Lazy Controller';
};
しかし、これらの最後の2つの例を本番環境で使用しないでください。
Jqueryを使用して$ routeProviderを解決することもできます
app.js
/* Module Creation */
var app = angular.module ('app', ['ngRoute']);
app.config(['$routeProvider', '$controllerProvider', function($routeProvider, $controllerProvider){
/*Creating a more synthesized form of service of $ controllerProvider.register*/
app.registerCtrl = $controllerProvider.register;
function loadScript(path) {
var result = $.Deferred(),
script = document.createElement("script");
script.async = "async";
script.type = "text/javascript";
script.src = path;
script.onload = script.onreadystatechange = function (_, isAbort) {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
if (isAbort)
result.reject();
else
result.resolve();
}
};
script.onerror = function () { result.reject(); };
document.querySelector("head").appendChild(script);
return result.promise();
}
function loader(arrayName){
return {
load: function($q){
var deferred = $q.defer(),
map = arrayName.map(function(name) {
return loadScript('js/controllers/'+name+".js");
});
$q.all(map).then(function(r){
deferred.resolve();
});
return deferred.promise;
}
};
}
$routeProvider
.when('/', {
templateUrl: 'views/foo.html',
resolve: loader(['foo'])
})
.when('/bar',{
templateUrl: 'views/bar.html',
controller: 'BarCtrl',
resolve: loader(['bar'])
})
.otherwise({
redirectTo: document.location.pathname
});
}]);
/views/foo.html
<section ng-controller='FooCtrl'>
{{text}}
</section>
js/controllers/foo.js
/*Here we use the synthesized version of $controllerProvider.register
to register the controller in view*/
app.registerCtrl('FooCtrl',function($scope){
$scope.text = 'Test';
});
/views/bar.html
<section>
{{text2}}
</section>
js/controllers/bar.js
app.registerCtrl('BarCtrl',function($scope){
$scope.text2 = 'Test';
});
最初はAndréBetioloの答えを利用しました。ただし、ajaxの読み込みが非ブロッキングであるため、スクリプトが読み込まれる前にビューがコントローラーを要求することがあるため、常に機能するとは限りません。
解決策として、すべてのスクリプトが正常にロードされるまで関数が戻らないように強制しました。これは一種のハックですが、解決が完了する前にロードが成功することを確認します。また、複数のコントローラーをロードすることもできます。
app.js
var app = angular.module ('app', ['ngRoute']);
app.config(['$routeProvider', '$controllerProvider', function($routeProvider, $controllerProvider){
/*Creating a more synthesized form of service of $ controllerProvider.register*/
app.registerCtrl = $controllerProvider.register;
//jquery to dynamically include controllers as needed
function controllers(controllers){
var numLoaded = 0;
for (i = 0; i < controllers.length; i++) {
$.ajaxSetup({async:false});
$.getScript('js/controllers/' + controllers[i] + '.js').success(function(){
numLoaded++;
if (numLoaded == controllers.length) {
return true; //only return after all scripts are loaded, this is blocking, and will fail if all scripts aren't loaded.
}
});
}
}
$routeProvider
.when('/', {
templateUrl: 'views/foo.html',
resolve: {
load: function () {
controllers(['foo'])
}
}
})
.when('/bar',{
templateUrl: 'views/bar.html',
controller: 'BarCtrl',
resolve: {
load: function () {
controllers(['bar','foo']) //you can load multiple controller files
}
}
})
.otherwise({
redirectTo: document.location.pathname
});
}]);
/views/foo.html
<section ng-controller='FooCtrl'>
{{text}}
</section>
/views/bar.html
<section ng-controller='BarCtrl'>
{{text2}}
</section>
<section ng-controller='FooCtrl'>
{{text}}
</section>
/controllers/bar.js
app.registerCtrl('BarCtrl',function($scope){
$scope.text2 = 'Test';
});
//// JConfigファイル--------
window.angularApp.config(function ($routeProvider,$controllerProvider,$compileProvider,$provide, azMessages) {
$routeProvider.when('/login', {
resolve: {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var deferred = $q.defer();
require([
//load required Js file here
], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
} ]
}
});
$routeProvider.otherwise({ redirectTo: '/login' });
window.angularApp.components = {
controller: $controllerProvider.register,
service: $provide.service,
directive: $compileProvider.directive
}
//コントローラーの宣言
angularApp.components.controller('DiscussionController',[function(){
}]);
純粋なAngularJS遅延ロードを使用できます。
「LazyService」を作成します。
var ng = angular.module('app');
ng.factory('lazyService', [ '$http', function($http) {
var jsPath = 'js/${ name }.js';
var promisesCache = {};
return {
loadScript: function(name) {
var path = jsPath.replace('${ name }', name);
var promise = promisesCache[name];
if (!promise) {
promise = $http.get(path);
promisesCache[name] = promise;
return promise.then(function(result) {
eval(result.data);
console.info('Loaded: ' + path);
});
}
return promise;
}
}
}]);
次に、構成を定義します。
var ng = angular.module('app', [ 'ngRoute' ]);
ng.config([ '$routeProvider', '$controllerProvider', '$provide', function($routeProvider, $controllerProvider, $provide) {
// Lazy loading
ng.lazy = {
controller: $controllerProvider.register,
//directive: $compileProvider.directive,
//filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
}
$routeProvider
.when('/', {
templateUrl: 'view/home.html'
})
.when('/vendor', {
templateUrl: 'view/vendor.html',
resolve: {
svc: [ 'lazyService', function(lazyService) {
return lazyService.loadScript('services/vendor');
}],
ctrl: [ 'lazyService', function(lazyService) {
return lazyService.loadScript('controllers/vendor');
}]
}
});
. . .
「js/services/vendor.js」で、次のようにサービスを作成します。
var ng = angular.module('app');
ng.lazy.service('vendorService', [ function() {
. . .
「js/controllers/vendor.js」で、次のようにコントローラーを作成します。
var ng = angular.module('app');
ng.lazy.controller('vendorController', [ function() {
. . .
「resolve」プロパティは、ルートがロードされる前にどのプロミスを解決するかを定義します。
あなたが求めていることを行う最善の方法は、代わりにディレクティブを使用し、適切なタイミングでバインドされるようにコントローラーとテンプレートを結び付けることです。現在、2番目の例で示したようにグローバル関数を宣言しない限り、適切なタイミングでlazy.htm
でバインドは行われません。
理想的には-Angularは、新しいバージョンではより頻繁に適用される可能性があるため、HTMLとJSを強制的に分離します。
RequireJSの使用が必要な場合があります http://solutionoptimist.com/2013/09/30/requirejs-angularjs-dependency-injection/
トリックのために試すことができます
ng-controller-controller="'lazy'"
または
HTMLで
ng-controller-controller = "myObject.controller"
どこかに注入する
$scope.myObject.controller = $controller('lazy', {$scope: $scope})
これを試してください plugin for Angular JS。オンデマンドでコントローラースクリプトを遅延ロードするのに役立ちます。
サンプルコードをお送りします。それは私のためにうまく機能しています。これを確認してください:
var myapp = angular.module('myapp', ['ngRoute']);
/* Module Creation */
var app = angular.module('app', ['ngRoute']);
app.config(['$routeProvider', '$controllerProvider', function ($routeProvider, $controllerProvider) {
app.register = {
controller: $controllerProvider.register,
//directive: $compileProvider.directive,
//filter: $filterProvider.register,
//factory: $provide.factory,
//service: $provide.service
};
// so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
// Here I cannot get the controller function directly so I
// need to loop through the module's _invokeQueue to get it
var queue = angular.module(moduleName)._invokeQueue;
for (var i = 0; i < queue.length; i++) {
var call = queue[i];
if (call[0] == "$controllerProvider" &&
call[1] == "register" &&
call[2][0] == controllerName) {
app.register.controller(controllerName, call[2][1]);
}
}
}
var tt = {
loadScript:
function (path) {
var result = $.Deferred(),
script = document.createElement("script");
script.async = "async";
script.type = "text/javascript";
script.src = path;
script.onload = script.onreadystatechange = function (_, isAbort) {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
if (isAbort)
result.reject();
else {
result.resolve();
}
}
};
script.onerror = function () { result.reject(); };
document.querySelector(".shubham").appendChild(script);
return result.promise();
}
}
function stripScripts(s) {
var div = document.querySelector(".shubham");
div.innerHTML = s;
var scripts = div.getElementsByTagName('script');
var i = scripts.length;
while (i--) {
scripts[i].parentNode.removeChild(scripts[i]);
}
return div.innerHTML;
}
function loader(arrayName) {
return {
load: function ($q) {
stripScripts(''); // This Function Remove javascript from Local
var deferred = $q.defer(),
map = arrayName.map(function (obj) {
return tt.loadScript(obj.path)
.then(function () {
registerController(obj.module, obj.controller);
})
});
$q.all(map).then(function (r) {
deferred.resolve();
});
return deferred.promise;
}
};
};
$routeProvider
.when('/first', {
templateUrl: '/Views/foo.html',
resolve: loader([{ controller: 'FirstController', path: '/MyScripts/FirstController.js', module: 'app' },
{ controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' }])
})
.when('/second', {
templateUrl: '/Views/bar.html',
resolve: loader([{ controller: 'SecondController', path: '/MyScripts/SecondController.js', module: 'app' },
{ controller: 'A', path: '/MyScripts/anotherModuleController.js', module: 'myapp' }])
})
.otherwise({
redirectTo: document.location.pathname
});
}])
そして、HTMLページで:
<body ng-app="app">
<div class="container example">
<!--ng-controller="testController"-->
<h3>Hello</h3>
<table>
<tr>
<td><a href="#/first">First Page </a></td>
<td><a href="#/second">Second Page</a></td>
</tr>
</table>
<div id="ng-view" class="wrapper_inside" ng-view>
</div>
<div class="shubham">
</div>
</div>
ありがとう