HOME > 開発者向けBLOG > Sencha Blog >  Ext JS 5 と Sencha Touch でコードを再利用して Sencha Space アプリを作る

Technology Note 開発者向けBLOG

Sencha Blog

Ext JS 5 と Sencha Touch でコードを再利用して Sencha Space アプリを作る

こんにちは、ゼノフィnakamuraです。

この記事は、US Sencha社ブログ Reusing Code in Ext JS 5 and Sencha Touch Apps to Build a Sencha Space App を翻訳したものです。

Ext JS 5 によって、モバイルアプリケーション (Sencha Touch) とデスクトップ/タブレットアプリケーション (Ext JS) のコードを共有するという夢が実現できました。この記事では、Sencha Cmd 5 を使って、Ext JS 5 と Sencha Touch 2 とでコードを共有する Sencha Space アプリケーションを作成します。

この記事の目的は次の通りです。

  • 出来るだけコードを再利用する Sencha Touch と Ext JS アプリケーションを作成する
  • 共有機能が含まれているパッケージを利用する
  • 非互換性の扱い方を学ぶ
  • Sencha Space の環境でアプリケーションをデプロイする

目的に到達するために、グリッドとチャートの形式で 2012年の最も人気な Web ブラウザのデータを表示するアプリケーションを生成します。この記事のソースコードは全てフォーク/ダウンロードできます。

準備

成功するためには準備するすることが重要です。 ワークスペースとパッケージは 以前からありますが、Ext JS 5 では、複数アプリケーションの環境をよりクリアにします。

さて、まずはワークスペースを作りましょう。Sencha Cmd 5 がインストールされていることを確認して下さい。

$ sencha generate workspace Ext5Tablet
Sencha Cmd v5.0.0.160
...

これでアプリケーションとフレームワークをホストするディレクトリができました。ワークスペースのディレクトリに移動して、次を実行してください:

gmac15:Ext5Tablet grgur$ sencha -sdk /path/to/ext-5.0.0 generate app MyApp ./ExtSpace

アプリケーション名を MyApp とします。この Ext JS 5 アプリケーションはタブレットユーザーむけになります。スマートフォンのユーザーをサポートするには、別の Sencha Touch アプリケーションを生成します:

$ sencha -sdk /path/to/touch-2.3.1-complete generate app MyApp ./TouchSpace

最後に、共有するコードを格納するパッケージを生成しましょう。その中にチャートやグリッド/リストコンポーネントをコーディングしますので、パッケージをGridChartと呼ぶことにします。ワークスペースディレクトリで次を実行して下さい:

$ sencha generate package GridChart

ファイル構造は次のようになります。

ExtSpaceTouchSpaceはアプリケーションフォルダで、packages/GridChart には共有するコンポーネントがあり、exttouch はフレームワークのSDKを参照します。次は各部分をもっと深く検討します。

共有チャートコンポーネント

Ext JS 5 は、Sencha Touch 2 から引き継いだ素晴らしい新たなチャートが含まれています。その上、コンフィグのパターンが非常に似ていますので、簡単に再利用できます。

Ext.chart.Chart ウィジェットで必要なチャートをインテリジェントにインスタンス化することが出来ますので、それを両方のフレームワークのために行えます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Ext.define('GC.Chart', {
    extend : 'Ext.chart.Chart',
    xtype  : 'gcchart',
 
    config : {
        animate      : true,
        background   : 'white',
        insetPadding : 40,
        interactions : 'itemhighlight',
        legend       : {
            docked : 'bottom'
        },
 
        colors: [...],
 
        series: [...],
        axes  : [...]
    }
});

完全なソースコードはここにあります。

注: Sencha Touchのチャートは Sencha Complete スイートの一部となります。

この例で二つのパターンが見えます。まずは、コードが共有されるということ。ということは、クラス定義は全て Sencha Touch 2 と Ext JS 5 とで互換性があるということです。

次に、Ext JS 5 は、config オブジェクトからのプロパティを理解でき、ブロック外のものにマッピングします。このチャートコンフィグは Sencha Touch にそっくりですが Ext JS でも無事に動作している理由がそれです。

ユニバーサルなチャートウィジェットができましたので、二つのフレームワークに対して、少しだけ違うところがある共有コードの生成に付いて検討できます。

Grid vs. List

グリッドのようなビューで、データを表示したい。Ext JS grid ウィジェットはタブレット向けのタスクを実行し、Sencha Touch List はスマートフォン用にデータを表示します。しかし、両方のコードは同じパッケージに存在します。

このアプローチの魅力は、GridChart パッケージの ‘package.json’ ファイルの1行に入っています。ワークスペースのディレクトリから ~/packages/GridChart/package.json を変更して、次の行を追加して下さい:

"classpath": "${package.dir}/src,${package.dir}/src-${framework.name}"

‘src-${framework.name}’ という部分によって、それぞれのフレームワークのソースコードを保存する二つのフォルダを作ることができます。そのフォルダ名は ‘src-touch’ (List) と ‘src-ext’ (Grid) となります。

双方のウィジェットはとてもシンプルになります。タブレット用のグリッドの設定は次のようになります:

Ext.define('GC.Grid', {
    extend : 'Ext.grid.Panel',
    xtype  : 'gcgrid',
 
    title       : 'Browsers',
    split       : true,
    collapsible : true,
    columns     : [
        {
            text      : 'Month',
            flex      : 1,
            dataIndex : 'month'
        },
        {
            header    : 'IE',
            xtype     : 'numbercolumn',
            width     : 70,
            dataIndex : 'data1',
            format    : '0,0%'
        },
        {...},
        {...},
        {...}
    ]
});

完全なソースコードはここにあります。

また、List ではテンプレートがほとんどを占めます。

Ext.define('GC.Grid', {
    extend : 'Ext.List',
    xtype  : 'gcgrid',
 
    config : {
        cls     : 'browserlist',
        itemTpl : [
            '<h1>{month}</h1>',
            '<div class="browsers">',
                '<b>IE</b> {data1} ',
                '<b>FF</b> {data2} ',
                '<b>Chrome</b> {data3} ',
                '<b>Safari</b> {data4} ',
             '</div>'
        ].join('')
    }
});

完全なソースコードはここにあります。

データ表示のシンプルなアプローチと組み合わせることができます。次のセクションでは、各アプリケーションのプラットフォーム特定のコードに、このコードを挿入することの説明をします。

タブレットレイアウト

共有コードはアプリの大きい部分を占め、そこにほとんどのビジネスロジックが含まれています。コードを挿入するのは簡単で、Ext JS アプリケーションの~/ExtSpace/app.json ファイルに一行を追加するだけです。

     "requires": [
        "sencha-charts",
        "GridChart"
    ],

完全ソースコードはここにあります。

Ext JS 5 は依存性に関して、モジュール的なアプローチを取ります。他にも ‘sencha-charts’ パッケージを利用したいでしょう。Sencha Cmd 5 はそれぞれを見つけることが出来、アプリケーションでそのクラスが使用可能になります。

注: Ext JS 5 SDK ディレクトリを眺めると、packages フォルダには、ext-charts と sencha-charts があります。ext-charts は古いチャートのパッケージを指し、sencha-charts は新しく改善されたコードです。

Main ビューを編集する前に、Cmd 5 の新しい使い方があります。app watch です。ターミナルがアプリケーションのディレクトリに入っていることを確認し、 (例えば ~/ExtSpace) 次を実行して下さい:

$ sencha app watch
Sencha Cmd v5.0.0.160
[INF] Loading app json manifest...
...
create MyApp-all.css
[INF] Mapping http://localhost:1841/ to /Users/grgur/Projects/modus/modus-presos/Ext5Tablet...
[INF] ------------------------------------------------------------------
[INF] Starting web server at : http://localhost:1841
[INF] ------------------------------------------------------------------
[INF] Waiting for changes...

ベテランの開発者は既にこのコマンドをご存じでしょう。このコマンドは裏で SASS や JavaScript のファイルの変更を待って観察しています。変更があると、アプリケーションをアップデートするため必要なに最小限の操作 (CSS のコンパイルとかクラスメタデータのリフレッシュ) を行います。

さらに、このコマンドは組み込まれたJetty Web サーバーをインスタンス化してファイルをホストします。

全てが準備されたら、メインビューを生成できます。

Ext.define('MyApp.view.main.Main', {
    extend : 'Ext.container.Container',
    xtype : 'app-main',
 
    requires: [
        'GC.Chart',                     //#1
        'GC.Grid',                      //#1
        'Ext.data.JsonStore',
        'Ext.chart.*'
    ],
 
    layout : {                          //#3
        type : 'border'
    },
 
    config : {                          //#2
        grid : 'west',
 
        chart : 'center',
 
        store : [
            { month : 'Jan', data1 : 20, data2 : 37, data3 : 35, data4 : 4 },
            //...
            { month : 'Dec', data1 : 15, data2 : 31, data3 : 47, data4 : 4 }
        ]
    },
 
    /**
     * storeId または store のインスタンスを元にストアをインスタンス化
     */
    applyStore : function (data, store) {       // #5
        return Ext.isString(data) || data.isStore ? Ext.getStore(data) : Ext.factory({
            fields : ['month', 'data1', 'data2', 'data3', 'data4' ],
            data   : data
        }, 'Ext.data.JsonStore', store);
    },
 
    /**
     * 共有チャートをインスタンス化
     * @param region
     * @param chart
     * @returns {GC.Chart} Chart
     */
    applyChart : function (region, chart) { // #5
        return Ext.factory({
            region : region,
            width  : 400,
            store  : this.getStore()            // #4
        }, 'GC.Chart', chart);
    },
 
    /**
     * グリッドをインスタンス化
     */
    applyGrid : function (region, grid) {       // #5
        return Ext.factory({
            region : region,
            width  : 400,
            store  : this.getStore(),           // #4
            title  : 'Browsers in 2012'
        }, 'GC.Grid', grid);
    },
 
    /**
     * ビューにアイテムを追加
     */
    initComponent : function () {
        var me = this;
 
        me.items = [
            me.getChart(),
            me.getGrid()
        ];
 
        me.callParent();
    }
});

完全ソースコードはここにあります。

上記のコードをレビューすると、いくつかの問題点に気付くでしょう。

  1. 以前は GridChart パッケージがアプリにインクルードし、Sencha Cmd は位置を正しくマッピングすることが出来ました。そのパッケージのクラスを利用するためには、Sencha の規約通りにrequire する必要があります。
  2. Sencha Touch 2 と違って、Ext JS 4 のコンフィグオプションは config オブジェクトのメンバーではありません。複数フレームワークで使えるようにするために、Ext JS 5 ではコンストラクタの config オブジェクトからのパラメータはほとんど認識できるようになりました。この例では、store プロパティを再利用し、新しいもの2つ (gridとchart) を追加しています。grid と chart はどちらもボーダーレイアウトでの位置を表しています。
  3. レイアウトは例外です。Sencha Touch 2 と Ext JS 5 での Ext.Component ライフサイクルの違いのため、layout コンフィグは config ブロックの外ではないいけません。
  4. grid と chart コンポーネントは同じデータストアを再利用します。
  5. factory メソッドと appliers を利用する、Sencha Touch モジュール コンポーネント インスタンス化は Ext JS でも対応となっています。applyStore, applyChart, applyGrid メソッドをご覧下さい。

コツ: パッケージの共有コンポーネントを生成する際には、‘constructor’ をオーバーライドできます。

constructor: function (config) {
    config.layout = 'border';
    this.callParent([config]);
}

ほとんどの作業は終わりました。Ext JS 5 のアプリケーションをブラウザで実行すると、次のようなものが表示されます。

このビューを Sencha Touch に複製しましょう。

スマートフォンのレイアウト

ビューのコードを追加する前に、このアプリケーションの ~/TouchSpace/app.json にある app.jsonを確認します。

完全ソースコードはここにあります。

今回は sencha-charts を参照する必要はありません、なぜならチャートは Sencha Touch 2.3 のパッケージの形式ではないからです。

sencha app watch を実行すると、このアプリが効率よく更新されるようになります。

ここで自動生成されたMain ビューを編集して、次の様に変更します。

Ext.define('MyApp.view.Main', {
    extend   : 'Ext.Container',
    xtype    : 'main',
    requires : [
        'GC.Chart',
        'GC.Grid',
        'Ext.data.JsonStore',
        'Ext.chart.*'
    ],
    config   : {
        layout: {
            type: 'hbox',
            align: 'stretch'
        },
 
        grid  : 1,
        chart : 2,
        store : [...]
    },
 
    applyStore : function (data, store) {...},
 
    applyChart : function (flex, chart) {
        return Ext.factory({
            flex  : flex,
            store : this.getStore()
        }, 'GC.Chart', chart);
    },
 
    applyGrid : function (flex, grid) {...},
 
    /**
     * Add items to the view
     */
    initialize : function () {
        var me = this,
            grid = me.getGrid(),
            chart = me.getChart();
 
        me.add([
            grid,
            chart
        ]);
 
        me.callParent();
    }
});

完全なソースコードはここ にあります。

ボーダーレイアウトの代わりに、HBox を使いました。他は全てアプリケーションのExt JS バージョンで利用したコードに似ています。

主な違いは、initialize と initComponent ブロックにあり、Ext JS では this.items プロパティをオーバーライドする必要があり、Sencha Touch では add メソッドを使っています。 この理由は単純で、Sencha Touch はインスタンス化の最中にDOM エレメントを生成しますが、Ext JS はレンダリング中にこれを行い、これは DOM にウィジェットを挿入する直前に行うからです。

Ext JS アプリケーションのコードをとても多く共有したので、数分間でよく似た UI を開発できました。ブラウザで動作させると、次のようになるでしょう。

Sencha Spaceへのデプロイ

Sencha Space は保護された、モバイル Web や HTML5 アプリケーションのクロスプラットフォーム環境で、組織がエンドユーザーに企業アプリケーションを提供する際に利用できます。Sencha Space をまだご存知ではなければ、 Sencha Spaceの詳しい説明(Webセミナー) on Vimeo をご覧ください (日本語字幕付き) 。

我々の新しいアプリケーションが、このエコシステムの一部になることで、Sencha SpaceのAPIを利用して、他のアプリケーションと通信することも可能となるという 利点があります。

既にアカウントを持っているとして、 Sencha Space Management Console にログインします。 ここから、アプリケーションを追加し、 あなたのローカルウェブサーバーを指していることを確認して下さい。 まだアプリケーションを一般公開する必要はありませんが、デバッグに役に立ちます。

両方のアプリケーションに (下の図のように) invoke intent の文字列を設定して下さい。このデモは、スマホ用アプリ (Sencha Touch) は *bpmobile* を使用し、タブレット用アプリ (Ext JS) は、*bprich* を利用しています。

Sencha Space API のパスを追加するために、再び両方のアプリケーションの app.json ファイルを編集します。デバッグしたい場合には、あなたのローカル Weinre インスタンスへのパスを追加することもできます。

"js": [
    {
        "path": "http://space.sencha.io/space.js",
        "remote": true
    },
//    ...
]

Ext JS 5 アプリSencha Touch 2 アプリの完全なコードがあります。

最後に素晴らしいことがあります。我々のアプリケーションがデバイスを検出し、ユーザーにもっと最適な体験を得るためには、他のアプリケーションに切り替えるようにプロンプトを出そうと思います。例えば、Ext JS のアプリケーションにスマホでアクセスするユーザーは、その人の端末に適切なソリューションがあることを通知するべきです。

あなたの Ext JS アプリケーションの ~/ExtSpace/app/Application.js ファイルに次の追加をします。

launch : function () {
    var me = this;
 
    Ext.onSpaceReady(function () {
        var width = Ext.getBody().getWidth();
        if (width < 1000) {
            me.transferToMobile();
        }
    });
},
 
transferToMobile : function () {
    Ext.Msg.confirm('Phone detected', 'Do you want to see the phone site instead?', function (resp) {
        if (resp === 'yes') {
            Ext.space.Invoke.get('bpmobile').then(function (connection) {
                connection.send({}, true);
            }, console.warn.bind(console));
        }
    });
}

完全なソースコードはここ にあります。

Sencha Space の環境が使用可能になったら、Ext.onSpaceReady が発火します。これはドキュメントが使用可能になった後、数ミリ秒内で起こります。ハンドラーが最初に確認メッセージをプロンプトし、その後は Invoke API を利用して Touch のアプリケーションをフォアグラウンドに持って来ます。どのアプリケーションと通信するか指定するinvoke 文字列 bpmobile に注目して下さい。

さて、ゆっくりして、我々のコード作業の結果を見ましょう!

日本語字幕付き Vimeo.

ソースコード

このアプリケーションの完全に動作するソースコードはここで入手可能です。これをブラウザまたは Sencha Space インスタンスに読み込むだけです。

まとめ

Ext JS 5 はエンタプライズアプリケーションに対して、多くの新しい機能を提供しています。その一つは Sencha Touch との統合とコードの共有です。しかし、統合の可能性は Sencha Cmd 5 と Sencha Space を利用するとより拡張できますので、どのエンタプライズ環境でも満足の行く、最も機能性が豊かなフレームワークを提供しています。この例では、Sencha Touch 2 から受け継いだ新しい Sencha Chart の機能の力も目撃できました。Ext JS 5 は、デスクトップとタブレットデバイスの両方でスムーズで申し分ないユーザー体験を提供します。

PAGETOP