Require.jsをAngular.jsと組み合わせて使用しています。
たとえば、FirstController
には Angular UI Codemirror が必要です。それは少なくとも135 kbです。
require([
"angular",
"angular.ui.codemirror" // requires codemirror itself
], function(angular) {
angular.module("app", [ ..., "ui.codemirror" ]).controller("FirstController", [ ... ]);
});
Angular幸せにするために、ページがロードされるたびにディレクティブとCodemirror libを含める必要はありません。
だからこそ、ルートがヒットしたときにのみコントローラーをロードするのです ここで行われていることのように 。
ただし、次のようなものが必要な場合
define([
"app",
"angular.ui.codemirror"
], function(app) {
// ui-codemirror directive MUST be available to the view of this controller as of now
app.lazy.controller("FirstController", [
"$scope",
function($scope) {
// ...
}
]);
});
Angularを挿入してui.codemirror
モジュール(または他のモジュール)もappモジュールにありますか?
外部依存関係のコードの変更を伴う場合を除き、これを達成するためのハック的な方法であるかどうかは気にしません。
それが便利な場合:私はAngular 1.2.0。
私は今、しばらくの間requirejs + Angularをミックスしようとしています。スコープがインラインコードやフィドルには大きすぎるため、これまでの努力でGithubに小さなプロジェクト( angular-require-lazy )を公開しました。このプロジェクトは、次の点を示しています。
方法は次のとおりです。
$controllerProvider
_、_$compileProvider
_)は、config
関数からキャプチャされます(最初に angularjs-requirejs-lazy-controllers )。angular
は、遅延ロードされたモジュールを処理できる独自のラッパーに置き換えられます。この実装はあなたのニーズを満たします:lazy-load Angular modules(少なくとも私が使用しているng-grid))、間違いなくハックです:)、外部ライブラリを変更しません。
コメント/意見は大歓迎です。
(編集)このソリューションと他のソリューションの違いは、動的なrequire()
呼び出しを行わないため、r.js(およびrequire-lazyプロジェクト)でビルドできることです。それ以外の点では、アイデアはさまざまなソリューションにほぼ収束しています。
皆さんお元気で!
注意:Nikos Paraskevopoulosのソリューションを使用してください。信頼性が高く(使用しています)、さらに多くの例があります。
さて、私はついにこれを簡単な助けでこれを達成する方法を見つけました answer 。
私の質問で言ったように、これは非常にハックな方法になりました。アプリモジュールのコンテキストで、依存モジュールの_invokeQueue
配列内の各関数を適用します。
これは次のようなものです(moduleExtender関数にもっと注意を払ってください):
define([ "angular" ], function( angular ) {
// Returns a angular module, searching for its name, if it's a string
function get( name ) {
if ( typeof name === "string" ) {
return angular.module( name );
}
return name;
};
var moduleExtender = function( sourceModule ) {
var modules = Array.prototype.slice.call( arguments );
// Take sourceModule out of the array
modules.shift();
// Parse the source module
sourceModule = get( sourceModule );
if ( !sourceModule._amdDecorated ) {
throw new Error( "Can't extend a module which hasn't been decorated." );
}
// Merge all modules into the source module
modules.forEach(function( module ) {
module = get( module );
module._invokeQueue.reverse().forEach(function( call ) {
// call is in format [ provider, function, args ]
var provider = sourceModule._lazyProviders[ call[ 0 ] ];
// Same as for example $controllerProvider.register("Ctrl", function() { ... })
provider && provider[ call[ 1 ] ].apply( provider, call[ 2 ] );
});
});
};
var moduleDecorator = function( module ) {
module = get( module );
module.extend = moduleExtender.bind( null, module );
// Add config to decorate with lazy providers
module.config([
"$compileProvider",
"$controllerProvider",
"$filterProvider",
"$provide",
function( $compileProvider, $controllerProvider, $filterProvider, $provide ) {
module._lazyProviders = {
$compileProvider: $compileProvider,
$controllerProvider: $controllerProvider,
$filterProvider: $filterProvider,
$provide: $provide
};
module.lazy = {
// ...controller, directive, etc, all functions to define something in angular are here, just like the project mentioned in the question
};
module._amdDecorated = true;
}
]);
};
// Tadaaa, all done!
return {
decorate: moduleDecorator
};
});
これが完了したら、たとえば、これを行うために必要なだけです:
app.extend( "ui.codemirror" ); // ui.codemirror module will now be available in my application
app.controller( "FirstController", [ ..., function() { });
これの鍵は、app
モジュールが依存するモジュールも遅延読み込みモジュールである必要があることです。これは、angularが$ injectorサービスに使用するプロバイダーおよびインスタンスのキャッシュがプライベートであり、初期化の完了後に新しいモジュールを登録するメソッドを公開しないためです。
したがって、これを行うための「ハッキング」方法は、遅延ロードするモジュールのそれぞれを編集して、遅延ロードモジュールオブジェクトを要求することです(リンクした例では、モジュールは「appModules.js」ファイルにあります)。次に、コントローラー、ディレクティブ、ファクトリーなどの各呼び出しを編集して、app.lazy.{same call}
代わりに。
その後、アプリルートがどのように遅延ロードされるかを調べることで、リンクしたサンプルプロジェクトを引き続き追跡できます(「appRoutes.js」ファイルにこれを行う方法が示されています)。
これが役立つかどうかはわかりませんが、幸運を祈ります。
これを行うディレクティブがあります:
https://github.com/AndyGrom/loadOnDemand
例:
<div load-on-demand="'module_name'"></div>
既存の遅延ロード技術の問題は、私がやりたいことを自分でやることです。
たとえば、requirejsを使用して、次のように呼び出します。
require(['tinymce', function() {
// here I would like to just have tinymce module loaded and working
});
ただし、その方法では機能しません。どうして?私が理解しているように、AngularJSはモジュールを「将来ロードされる」とマークするだけです。たとえば、少し待てば機能します。使用できます。したがって、上記の関数では、loadPendingModules()などの関数を呼び出したいと思います。
私のプロジェクトでは、このことだけを行うシンプルなプロバイダー( 'lazyLoad')を作成しました。そのため、モジュールを完全にロードする必要がある場合は、次の操作を実行できます。
myApp.controller('myController', ['$scope', 'lazyLoad', function($scope, lazyLoad) {
// ........
$scope.onMyButtonClicked = function() {
require(['tinymce', function() {
lazyLoad.loadModules();
// and here I can work with the modules as they are completely loaded
}]);
};
// ........
});
ソースファイルへのリンク(MPLライセンス): https://github.com/lessmarkup/less-markup/blob/master/LessMarkup/UserInterface/Scripts/Providers/lazyload.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>