JavaScriptアプリケーション用のDependency Injection コンテナ「ContaienrJS」の新版をリリース
夏休みの宿題ということで、作成してから長らくメンテナンスしていなかった ContaienrJS の新版をリリースしました。変更点は以下です。
- 新機能 : モジュールの非同期遅延読み込みに対応
- require.jsと連携し、コンポーネント(=コンテナ管理下のオブジェクト)が実際に必要とされるまで、そのJavaScriptソースの読み込みと評価を遅延します。
- ユーザーがボタンを押したらダイアログを表示するようなシーンで、ボタンが押されたタイミングでダイアログクラスをロードしてインスタンスを作成するといった制御が容易に実現できます。
- ↑これに伴い、
ダウンロード
ダウンロードは以下より。
unageanu / container-js - GitHub
ドキュメントはここにあります。DIコンテナとしての基本的な機能についてはこちらを参照ください。Aspectとかにも対応してます。
サンプル
具体的に動くものをいくつか。
samples/ 以下に完全なソースがあるのでそちらも参照ください。
Hello World
「Hello World」を出力するサンプルです。
ファイル構造:
├ index.html └ scripts/ ├ main.js ├ require.js ├ container.js ├ app/ │ ├ model.js │ └ view.js └ utils/ └ observable.js
scripts/app/model.js:
define(["utils/observable"], function(Observable){ "use strict"; /** * @class */ var Model = function() {}; Model.prototype = new Observable(); /** * @public */ Model.prototype.initialize = function() { this.fire( "updated", { property: "message", value :"hello world." }); }; return Model; });
scripts/app/view.js:
define(["container"], function( ContainerJS ){ "use strict"; /** * @class */ var View = function() { this.model = ContainerJS.Inject("app.Model"); }; /** * @public */ View.prototype.initialize = function() { this.model.addObserver("updated", function( ev ) { if ( ev.property != "message" ) return; var e = document.getElementById(this.elementId); e.innerHTML = ev.value; }.bind(this)); }; return View; });
scripts/app/main.js:
require.config({ baseUrl: "scripts", }); require(["container"], function( ContainerJS ) { var container = new ContainerJS.Container( function( binder ){ binder.bind("app.View").withProperties({ elementId : "console" }).onInitialize("initialize") .inScope(ContainerJS.Scope.EAGER_SINGLETON); binder.bind("app.Model"); }); window.addEventListener("load", function() { container.get("app.Model").then(function( model ){ model.initialize(); }, function( error ) { alert( error.toString() ); }); }, false); });
index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello World</title> <script type="text/javascript" data-main="scripts/main.js" src="scripts/require.js"></script> </head> <body> <div id="console"></div> </body> </html>
Lazy Loading
遅延読み込みのサンプルです。
リンクがクリックされたら、component.jsの読み込みとインスタンス化が行われます。
ファイル構造:
├ index.html └ scripts/ ├ main.js ├ require.js ├ container.js └ app/ ├ component.js └ owner.js
scripts/app/component.js:
define(function(){ "use strict"; /** * @class */ var Component = function() { print("component : created."); }; return Component; });
scripts/app/owner.js:
define(["container"], function( ContainerJS ){ "use strict"; /** * @class */ var Owner = function() { this.component = ContainerJS.Inject.lazily("app.Component"); print("owner : created."); }; /** * @public */ Owner.prototype.initialize = function() { print( "owner : initialize." ); this.component.then(function( component){ }, function( error ) { alert( error.toString() ); }); }; return Owner; });
scripts/main.js:
require.config({ baseUrl: "scripts" }); require(["container"], function( ContainerJS ) { window.print = function( message ) { document.getElementById("console").innerHTML += message + "<br/>"; }; var container = new ContainerJS.Container( function( binder ){ binder.bind("app.Component"); binder.bind("app.Owner"); }); window.addEventListener("load", function() { container.get("app.Owner").then(function( owner ){ document.getElementById("link").addEventListener( "click", function(){ owner.initialize(); }); }, function( error ) { alert( error.toString() ); }); }, false); });
index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Lazy Loading</title> <script type="text/javascript" data-main="scripts/main.js" src="scripts/require.js"></script> </head> <body> <div> <a id="link" href="javascript:void(0);">click.</a> </div> <div id="console"></div> </body> </html>
Method Interception
前からある機能ですが、コンポーネントのメソッドにインターセプタを差し込めます。
ファイル構造:
├ index.html └ scripts/ ├ main.js ├ require.js ├ container.js └ app/ └ component.js
scripts/app/component.js:
define(function(){ "use strict"; /** * @class */ var Component = function() {}; Component.prototype.method1 = function( ) { print("method1 : executed."); }; Component.prototype.method2 = function( ) { print("method2 : executed."); }; return Component; });
scripts/main.js:
require.config({ baseUrl: "scripts" }); require(["container"], function( ContainerJS ) { window.print = function( message ) { document.getElementById("console").innerHTML += message + "<br/>"; }; var container = new ContainerJS.Container( function( binder ){ binder.bind("app.Component"); binder.bindInterceptor( function( jointpoint ) { print( "before : " + jointpoint.methodName ); var result = jointpoint.proceed(); print( "after : " + jointpoint.methodName ); return result; }, function(binding, component, methodName) { if (binding.name !== "app.Component" ) return false; return methodName === "method1" || methodName === "method2"; } ); }); window.addEventListener("load", function() { container.get("app.Component").then(function( component ){ document.getElementById("link1").addEventListener( "click", function(){ component.method1(); }); document.getElementById("link2").addEventListener( "click", function(){ component.method2(); }); }, function( error ) { alert( error.toString() ); }); }, false); });
index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Method Interception</title> <script type="text/javascript" data-main="scripts/main.js" src="scripts/require.js"></script> </head> <body> <div> <a id="link1" href="javascript:void(0);">click to execute method1.</a>| <a id="link2" href="javascript:void(0);">click to execute method2.</a> </div> <div id="console"></div> </body> </html>