初心者のためのExtJS入門

ExtJSを使うので、ついでにまとめていきます

ビューモデルのデータにモデルを設定して使用する

ビューモデルに、データのモデルを設定することがあります。

今回は、その際の使い方や注意点を取り上げます。

使用するモデルやデータは下記の通りです。

Ext.define('Sample.model.Company', {
    extend: 'Ext.data.Model',

    fields: [
        {
            name: 'id',
            type: 'int'
        },
        {
            name: 'name',
            type: 'string'
        },
        {
            name: 'addressId',
            reference: 'Sample.model.Address'
        }
    ],

    hasMany: [
        {
            name: 'children',
            model: 'Sample.model.Company'
        }
    ],

    proxy: {
        type: 'ajax',
        url: '/dummy/company.json',
        reader: {
            type: 'json',
            rootProperty: 'data'
        }
    }
});

Ext.define('Sample.model.Address', {
    extend: 'Ext.data.Model',

    fields: [
        {
            name: 'id',
            type: 'int'
        },
        {
            name: 'postalCode',
            type: 'string'
        },
        {
            name: 'state',
            type: 'string'
        }
    ]
});

Ext.define('Sample.view.sample.PanelModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.sample_panel',

    data: {
        /**
         * @cfg {Sample.model.Company}
         */
        record: null
    }
});

Ext.define('Sample.view.sample.PanelController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.sample_panel',

    control: {
        '#': {
            afterrender: 'onAfterRender'
        }
    },

    onAfterRender: function () {
        var me = this,
            viewModel = me.getViewModel();

        // データをロードして、ビューモデルに設定
        Sample.model.Company.load(null, {
            success: function (record) {
                viewModel.set('record', record);
            }
        });
    }
});
{
  "success": true,
  "data": {
    "id": 1,
    "name": "XXX会社",
    "address": {
      "id": 1000,
      "postalCode": "1234567",
      "state": "鹿児島県"
    },
    "children": [
      {
        "id": 2,
        "name": "YYY会社"
      },
      {
        "id": 3,
        "name": "ZZZ会社"
      }
    ]
  }
}

モデルのプロパティをバインディング

{record.name}のように記述することで、モデルのプロパティをバインディングできます。

アソシエーションのモデルでも同様です。

Ext.define('Sample.view.sample.Panel', {
    extend: 'Ext.panel.Panel',
    xtype: 'sample_panel',

    cls: 'sample-panel',

    controller: 'sample_panel',
    viewModel: 'sample_panel',

    bodyPadding: 15,

    layout: 'anchor',

    defaults: {
        anchor: '100%'
    },

    items: [
        {
            xtype: 'displayfield',
            fieldLabel: '会社名',
            bind: '{record.name}'
        },
        {
            xtype: 'displayfield',
            fieldLabel: '郵便番号',
            bind: '{record.address.postalCode}'
        }
    ]
});

f:id:sham-memo:20171112131220p:plain

モデルの値が変更されても、ちゃんとビューに反映されます。

onClickChangeButton: function () {
    var me = this,
        viewModel = me.getViewModel(),
        record = viewModel.get('record');

    record.set('name', 'ABC会社');
    record.getAddress().set('postalCode', '9999999');
}

f:id:sham-memo:20171112132219p:plain

アソシエーションのストアをバインディング

プロパティと同じようにして、アソシエーションのストアもバインディングできます。

Ext.define('Sample.view.sample.Panel', {
    extend: 'Ext.panel.Panel',
    xtype: 'sample_panel',

    cls: 'sample-panel',

    controller: 'sample_panel',
    viewModel: 'sample_panel',

    bodyPadding: 15,

    layout: 'anchor',

    defaults: {
        anchor: '100%'
    },

    items: [
        {
            xtype: 'dataview',
            bind: {
                store: '{record.children}'
            },
            itemTpl: '<div class="child-company">{name}</div>',
            itemSelector: '.child-company'
        }
    ]
});

f:id:sham-memo:20171112132642p:plain

注意点

formulasを使うときに注意することがあります。

例えば、下記のようなコードを書いたとします。

Ext.define('Sample.view.sample.PanelModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.sample_panel',

    data: {
        /**
         * @cfg {Sample.model.Company}
         */
        record: null
    },

    formulas: {
        // bad...
        escapeName: function (get) {
            var record = get('record');
            return record ? Ext.String.htmlEncode(record.get('name')) : null;
        },

        // bad...
        fullAddress: function (get) {
            var record = get('record'),
                address, text;

            if (record) {
                address = record.getAddress();
                text = address.get('state') + address.get('cities') + address.get('apartment');
            }

            return text;
        }
    }
});

Ext.define('Sample.view.sample.Panel', {
    extend: 'Ext.panel.Panel',
    xtype: 'sample_panel',

    cls: 'sample-panel',

    controller: 'sample_panel',
    viewModel: 'sample_panel',

    bodyPadding: 15,

    layout: 'anchor',

    defaults: {
        anchor: '100%'
    },

    dockedItems: {
        xtype: 'button',
        text: 'change',
        handler: 'onClickChangeButton'
    },

    items: [
        {
            xtype: 'displayfield',
            fieldLabel: '会社名',
            bind: '{escapeName}'
        },
        {
            xtype: 'displayfield',
            fieldLabel: '郵便番号',
            bind: '{record.address.postalCode}'
        },
        {
            xtype: 'displayfield',
            fieldLabel: '住所',
            bind: '{fullAddress}'
        }
    ]
});

f:id:sham-memo:20171112133554p:plain

これは一見すると良さそうですが、下記のような値の変更でビューが更新されません。

onClickChangeButton: function () {
    var me = this,
        viewModel = me.getViewModel(),
        record = viewModel.get('record');

    record.set('name', 'ABC会社');
    record.getAddress().set('state', '東京都');
}

get('record.address.state')のように、直接対象のプロパティを取得するように書き直すことで改善します。

Ext.define('Sample.view.sample.PanelModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.sample_panel',

    data: {
        /**
         * @cfg {Sample.model.Company}
         */
        record: null
    },

    formulas: {
        // good!!
        escapeName: function (get) {
            return Ext.String.htmlEncode(get('record.name') || '');
        },

        // good!!
        fullAddress: function (get) {
            var state = get('record.address.state'),
                cities = get('record.address.cities'),
                apartment = get('record.address.apartment');

            return state + cities + apartment;
        }
    }
});

うっかり間違えてしまう典型的なパターンなので注意するようにしましょう。