Ext JS 5 データパッケージ

Sencha Advent Calendar 2014 も残すところ、2つとなりました。

もちろん、大トリは、@kotsutsumi さんですので、私が前座を取らせてもらいます。

この前の記事でご紹介した THE KIDDIE ですが、あのあとも色々と聴いているんですが、いいんですよ。楽曲もいいし、歌うまいし。皆さんも聴いてみて下さい。 最新曲 OMELAS の PV がこちらです。明日は、渋谷公会堂でライブです。行きたかったなぁ〜。

僕が、お気に入りなのは「溜息の呪文」て曲ですので、今回のまくらでは Sencha な呪文ぽい話をしようかなと思います。

Sencha Cmd 5 と、Sencha Ext JS 5 で開発していると、 bootstrap.json とかの変更が頻繁に必要になります。 いちいちやるのはめんどくさいので、ここは、Sencha が推奨するように、「ウォッチ」を起動しましょう。

違〜う。それじゃない。

呪文その1

    sencha app watch

app watch は、 Sencha Cmd 4 からありましたが、5 では進化しています。 Sencha Cmd 5 では、これでプロジェクト ディレクトリの監視もしながら、Web サーバーも立ち上げてくれます。 開発中のプロダクトが、サーバーサイドを必要としない、あるいは、外部のデータリソースを利用するような場合は、Cmd が起動する Web サーバーで十分に対応できるはずです。

そして、ファイルの変化を監視して、適切にビルド処理をしてくれます。 もちろん sencha app build をするよりもずっと素早くやってくれます。

呪文その2

もう一つの呪文は、

    sencha app refresh

です。 Ext JS 5 with Sencha Cmd 5 で開発をしている時には、この呪文を頻繁に唱えましょう。 それだけで結構スムースに開発が進みます。 「あれ?おっかしーな」 と思ったら

    sencha app refresh

なんか、新しいコンポーネントを足したら

    sencha app refresh

SASS ファイルにスタイル追加したら

    sencha app refresh

まずは呪文を唱えましょう。 あとね、

呪文その3

   sencha app build develope

というのが、ちょっと意味がある呪文のようです。

development ビルドは実際にはビルドしません。それをしない代わりに、ビルド前の状態に bootstrap.js などを戻してくれます。 アプリの開発途中で production や testing でビルドした後に、ビルトインコンポーネントのスタイルの一部が適用されなくなってしまった、なんてときにこの呪文を唱えると解決します。覚えておきましょう。

まくらは、ここまでにして、今日は Ext JS 5 のデータパッケージのお話をします。

Ext JS 5 データパッケージ

Ext JS 5 では、色々なところが、前のバージョンから大きく変わりました。 一つは、僕の前のエントリでもご紹介した、MVVM の導入です。 ViewModel と viewController によって、アプリケーションを構造化しやすくなりました。

次に挙げるとすると、Ext JS の真骨頂たるコンポーネント、Grid ですね。 Ext JS の Grid は花形コンポーネントですので、今回も沢山進化しています。 その一つは、昨日の Advent Calendar で、紹介されていますね。 5.1 で導入された、スプレッドシート セレクションモデルです。

このセレクションモデルや、クリップボード プラグインは、非常に便利で素晴らしい機能です。

このエントリでは、もうちょっと地味な所を取り上げます。

Ext JS になって、地味に変わったところは、データパッケージです。 ここの部分て、Ext JS の見た目系から入っていると何が嬉しいの?という世界ですが、実際に開発をしていると、こういった部分に助けられることの方が多いです。

今回のデータパッケージで変わったところはいくつかありますが、その中からいくつかのトピックを取り上げましょう。

フィールドには、多くの機能が加わりました。

convert / calculate

以前からあった convert メソッドに、depends 指定が加わりました。 convert メソッドは、サーバーからのレスポンスを、変換する関数で、モデルからそのフィールドを参照するときに、この関数が実行されて値が取り出されます。

    {
        name: 'fullName',
        convert: function(value, record) {
            return me.get('first') + ' ' + me.get('last');
        }
    }

こんな感じで使えるんですが、convert にはレコード (モデルのインスタンス) が丸ごと渡されるので、 どこかのフィールドがセットされる毎に convert を走らせる必要がありました。 dipends コンフィグを指定すると、

    {
        name: 'fullName',
        convert: function(value, record) {
            return me.get('first') + ' ' + me.get('last');
        },
        depends: ['first', 'last']
    }

指定したフィールドに変化があったときだけ、convert が実行されるようになり、効率がアップします。 でも、もっと便利な指定方法も追加されました。それは、caluculate メソッドです。

    {
        name: 'fullName',
        calculate: function(data) {
            return data.first + ' ' + data.last';
        }
    }

この指定方法だと、依存するフィールドの判定をフレームワークが行ってくれます。 こちらの方が簡単ですね。

バリデーション

Ext JS 4.x でのバリデーションは、モデルの valudations 配列に指定していました。

    Ext.define('User', {
        extend: 'Ext.data.Model',

        config: {
            fields: [
                {name: 'name',     type: 'string'},
                {name: 'age',      type: 'int'},
                {name: 'phone',    type: 'string'},
                {name: 'gender',   type: 'string'},
                {name: 'username', type: 'string'},
                {name: 'alive',    type: 'boolean', defaultValue: true}
            ],

            validations: [
                {type: 'presence',  field: 'age'},
                {type: 'length',    field: 'name',     min: 2},
                {type: 'inclusion', field: 'gender',   list: ['Male', 'Female']},
                {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
                {type: 'format',    field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
            ]
        }
    });

    var instance = Ext.create('User', {
        name: 'Ed',
        gender: 'Male',
        username: 'edspencer'
    });

    var errors = instance.validate();

    console.log(errors);

Ext JS 5 では、validators コンフィグに指定します。 そして、validators は、モデルにも指定できますが、フィールドにも指定できます。

    // モデルに指定する
    validators: {
        age: {
            type: 'presence'
        },
        username: [{
            type: 'exclusion',
            list: ['Admin', 'Operator']
        }, {
            type: 'format',
            matcher: /([a-z]+)[0-9]{2,3}/
        }]
    }

このように、すっきりと記述できます。 でももっといいのは、fields コンフィグの中に指定する方法です。

    fields: [{ 
        name: 'name',
        validators: [{
            type: 'exclusion',
            list: ['root', 'admin'],
            message: '不正な名前'
        }]
    }, {
        name: 'weight',
        validators: [{
            type: 'range',
            min: 1,
            max: 300,
            message: 'weight は 1 から 300 でなければなりません'
        }, {
            type: 'presence',
            message: 'weight を指定する必要があります'
        }]
    }]

フィールドの定義としてまとめて定義できます。 よりすっきりしますね。

実は、単にすっきりするだけでなく、このことによりとても便利になる事があります。

カスタムフィールド

Ext JS 4.x でのフィールドは、Ext.data.Field クラスです。 フィールドのタイプは、そのクラスの一つのプロパティでした。 ですから、

    Ext.define('SomeModel', {
        extend: 'Ext.data.Model',

        fields: [
            {name: 'user_name', type: 'string'},
            {name: 'age',       type: 'int'},
            {name: 'birthday',  type: 'date'}
        ]
    })

こうした定義がある場合、3つのフィールドは、それぞれ Ext.data.Field クラスのインスタンスであり、type はそのプロパティになります。

Ext JS 5 では、上記の定義では、3つのフィールドはそれぞれ、Ext.data.field.StringExt.data.field.IntegerExt.data.field.Date というクラスのインスタンスになります。

これで何が嬉しいかというと、カスタムフィールドのクラスを定義して、使い回しができるようになるところです。

Sencha Touch や Ext JS 4 で、モデルでバリデーションのコードを書いていた方ならわかると思うのですが、そこかしこで同じようなバリデーションコードを書くことがありました。VTypes にカスタムのバリデーションを登録できるとはいうものの、バンドルされたバリデーション、例えば inclusion/exclusion などでリスト指定するような場合、そのリストは各モデルで定義しなければなりませんでした。 また、同じような convert メソッドをあちこちのモデル定義に書いた経験もあるでしょう。

カスタムフィールドクラスを定義できるようになったことで、それらのコードの重複を避ける事ができるようになりました。 カスタムフィールドの定義の中に、convertcaluculatevalidators を設定する事で、そのフィールドを使い回すことができるようになります。

性別のバリデーションを設定するために、顧客モデルにも、従業員モデルにも、同じ include の設定をする必要は無くなりました。性別を表すカスタムフィールドを作り、それを顧客モデル、従業員モデルで共有すれば良いのです。

    Ext.define('App.fields.Gender', {
        extend: 'Ext.data.field.String',
        alias: 'data.field.gender',
        validators: {
            type: 'inclusion',
            list: [ '男', '女' ]
        }
    });

このカスタムフィールドをモデルで使うには、type: 'gender' とするだけです。

チェーンドストア

Sencha のフレームワークでは、データパッケージとビューをバインドして、ストア (Ext.data.Store) の中にあるデータを、様々な形 (グリッド/データビュー/チャートなど) で表示できます。 ストアには、ソートやフィルタの機能がついていて、グリッドなどで簡単にフィルタリングやソーティングができます。 とても便利ですよね。

Ext JS 5 には、そのストアに、チェーンドストアというものが加わりました。

このチェーンドストアってなんでしょうか。 これは、データの実体を持たないストアのことです。 データの実体は、他のストアを参照します。

チェーンドストアでは、データの実体は持ちませんが、フィルタやソートの機能を、ソースのストアとは別に定義することができます。

これまで、一つのデータソースを違う観点 (フィルタ条件) で表示する必要がある場合は、2つのストアを用意する必要がありました。当然、メモリ内に配置されるデータもそれぞれで持つことになります。 チェーンドストアを使えば、データはソースのストアが持ち、チェーンドストアに違うフィルタをセットすることで、同じデータを違う観点で参照できます。

チェーンドストアを使う最も簡単な方法は、ViewModel の sotres で定義することです。

    stores: {
        users: {
            model: 'MyApp.model.User'
        },
        femailusers: {
            source: 'users',
            filters: [{
                property: 'gender', value: 'femail'
            }]
        }
        source: 'Users',
    }

stores コンフィグの中に source コンフィグを追加するだけで、そのストアはチェーンドストアになります。

明日は @kotsutsumi さんです。

それでは、みなさま良いお年を。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です