ビューモデルのデータにモデルを設定して使用する
ビューモデルに、データのモデルを設定することがあります。
今回は、その際の使い方や注意点を取り上げます。
使用するモデルやデータは下記の通りです。
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}' } ] });
モデルの値が変更されても、ちゃんとビューに反映されます。
onClickChangeButton: function () { var me = this, viewModel = me.getViewModel(), record = viewModel.get('record'); record.set('name', 'ABC会社'); record.getAddress().set('postalCode', '9999999'); }
アソシエーションのストアをバインディング
プロパティと同じようにして、アソシエーションのストアもバインディングできます。
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' } ] });
注意点
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}' } ] });
これは一見すると良さそうですが、下記のような値の変更でビューが更新されません。
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; } } });
うっかり間違えてしまう典型的なパターンなので注意するようにしましょう。