読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

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>

動作確認はこちら