extjs列表视图中的模型关联

时间:2013-02-21 16:17:01

标签: extjs extjs4 extjs-mvc

我有两个模型:Page和Department。我在extjs的列表视图中显示页面,我想在List视图中显示Department的Name而不是department_id。我没有得到实际将部门添加到页面VIA GUI的部分,只是通过直接的db insert语句,但我想至少能够在列表视图中显示部门名称。

到目前为止,我有以下内容,这是显示department_id

模型

Ext.define('ExtMVC.model.Department', {
    extend: 'Ext.data.Model',
    fields: ['name']
});

Ext.define('ExtMVC.model.Page', {
    extend: 'Ext.data.Model',
    fields: ['title','body','department_id'],
    associations: [
        {type: 'belongsTo', model: 'Department'}
    ]
});

存储

Ext.define('ExtMVC.store.Pages', {
    extend: 'Ext.data.Store',
    model: 'ExtMVC.model.Page',
    autoLoad: true,
    proxy: {
      type: 'rest',
      url: '/admin/pages',
      format: 'json'
    }
});
Ext.define('ExtMVC.store.Departments', {
    extend: 'Ext.data.Store',
    model: 'ExtMVC.model.Department',
    autoLoad: true,
    proxy: {
      type: 'rest',
      url: '/admin/departments',
      format: 'json'
    }
});

列表视图

Ext.define('ExtMVC.view.page.List' ,{
    extend: 'Ext.grid.Panel',
    alias : 'widget.pagelist',

    title : 'All Pages',
    store: 'Pages',

    initComponent: function() {
        this.tbar = [{
            text: 'Create Page', action: 'create'
        }];

        this.columns = [
            {header: 'Title',       dataIndex: 'title',       flex: 1},
            {header: 'Department',  dataIndex: 'department_id',  flex: 1}
        ];
        this.callParent(arguments);
    }
});

controller(fwiw)

Ext.define('ExtMVC.controller.Pages', {
    extend: 'Ext.app.Controller',

    init: function() {
      this.control({
            'pagelist': {
                itemdblclick: this.editPage
            },
            'pagelist > toolbar > button[action=create]': {
                click: this.onCreatePage
            },
            'pageadd button[action=save]': {
              click: this.doCreatePage
            },
            'pageedit button[action=save]': {
              click: this.updatePage
            }
        });
    },

    onCreatePage: function () {
      var view = Ext.widget('pageadd');
    },

    onPanelRendered: function() {
        console.log('The panel was rendered');
    },

    doCreatePage: function (button) {
      var win = button.up('window'),
      form = win.down('form'),
      values = form.getValues(),
      store = this.getPagesStore();
      if (form.getForm().isValid()) {
        store.add(values);
        win.close();
        this.getPagesStore().sync();
      }
    },

    updatePage: function (button) {
        var win = button.up('window'),
            form = win.down('form'),
            record = form.getRecord(),
            values = form.getValues(),
            store = this.getPagesStore();
        if (form.getForm().isValid()) {
            record.set(values);
            win.close();
            this.getPagesStore().sync();
        }
    },

    editPage: function(grid, record) {
      var view = Ext.widget('pageedit');
      view.down('form').loadRecord(record);
    },

    stores: [
        'Pages',
        'Departments'
    ],

    models: [
      'Page'

    ],

    views: [
        'page.List',
        'page.Add',
        'page.Edit'
    ]
});

3 个答案:

答案 0 :(得分:9)

Ext的关联明显没有设计用于商店,而是用于处理单个记录......所以,我同意已经说过的话,你最好在服务器端压扁你的模型。然而,有可能实现你想要的。

在您调用生成的getter方法(即getDepartment())之前,关联不会加载您的关联模型(即Department)。试图采用这种方式,即为商店中加载的每个Page记录调用此方法需要大量的黑客攻击,因为网格会对商店的refresh事件做出同步响应,而getDepartment()方法返回异步 ...

这就是为什么您必须在加载页面的同一请求中加载部门数据的原因。也就是说,您的服务器必须返回以下格式的记录:

{title: 'First Page', body: 'Lorem', department_id: 1, department: {name: 'Foo'}}

为了让您的Page模型的代理服务于此,您需要以这种方式配置关联:

Ext.define('ExtMVC.model.Page', {
    // ...
    associations: [{
        type: 'belongsTo'
        // You need the fully qualified name of your associated model here
        // ... which will prevent Ext from generating everything magically
        ,model: 'ExtMVC.model.Department'

        // So you must also configure the getter/setter names (if you need them)            
        ,getterName: 'getDepartment'

        // Child data will be loaded from this node (in the parent's data)
        ,associationKey: 'department'

        // Friendly name of the node in the associated data (would default to the FQ model name)
        ,name: 'department'
    }]
});

然后是非常难看的部分。您的网格列无法使用经典dataIndex属性访问关联数据。但是,如果已经加载了相关记录,则可以从TemplateColumn这样访问:

{
    header: 'Department'
    ,xtype: 'templatecolumn'
    ,tpl: '{department.name}'
}

不幸的是,这会阻止您使用一些您可能已全局配置的更合适的列类(日期列等)。此外,此列丢失了它所代表的模型字段的跟踪,这意味着基于内省的某些功能将无法发挥其魔力(例如,网格过滤器ux使用字段类型自动决定类型过滤器)。

但是在你暴露的特殊情况下,这无关紧要......

完整示例

这是你把它们放在一起时所给出的东西(或see it in action)......

Ext.define('ExtMVC.model.Department', {
    extend: 'Ext.data.Model',
    fields: ['name'],
    proxy: {
        type: 'memory'
        ,reader: 'json'
        ,data: [
            {id: 1, name: 'Foo'}
            ,{id: 2, name: 'Bar'}
            ,{id: 30, name: 'Baz'}
        ]
    }
});

Ext.define('ExtMVC.model.Page', {
    extend: 'Ext.data.Model',
    fields: ['title','body','department_id'],
    associations: [{
        type: 'belongsTo'
        ,model: 'ExtMVC.model.Department'
        ,getterName: 'getDepartment'
        ,associationKey: 'department'
        ,name: 'department'
    }],
    proxy: {
        type: 'memory'
        ,reader: 'json'
        ,data: [
            {title: 'First Page', body: 'Lorem', department_id: 1, department: {name: 'Foo'}}
            ,{title: 'Second Page', department: {name: 'Bar'}}
            ,{title: 'Last Page', department: {name: 'Baz'}}
        ]
    }
});

Ext.define('ExtMVC.store.Pages', {
    extend: 'Ext.data.Store',
    model: 'ExtMVC.model.Page',
    autoLoad: true
});

Ext.define('ExtMVC.view.page.List', {
    extend: 'Ext.grid.Panel',
    alias : 'widget.pagelist',

    title : 'All Pages',
    store: Ext.create('ExtMVC.store.Pages'),

    initComponent: function() {
        this.tbar = [{
            text: 'Create Page', action: 'create'
        }];

        this.columns = [
            {header: 'Title', dataIndex: 'title', flex: 1}
            ,{header: 'Department', xtype: 'templatecolumn', flex: 1, tpl: '{department.name}'}
        ];

        this.callParent(arguments);
    }
});

Ext.widget('pagelist', {renderTo: 'ct', height: 200});

答案 1 :(得分:1)

正如其中一条评论所提到的,在处理网格/列表时将部门名称绑定到Pages模型会简单得多。显示的非规范化并不是一件坏事。

另一种选择可能是逆转建模,因为你可以说一个部门'hasMany'页面。

这允许您在关系上定义主键和外键,然后您的百货商店将(每行)有一个自动'pages()'商店,其中包含子信息。

我通常是为主/详细信息表单执行此操作,我想将'pages()'绑定到列表/网格,但是将部门模型保留为表单上的主记录。

答案 2 :(得分:0)

为什么不应该在网格列上使用渲染器配置功能?

这意味着在应用程序启动时(自动)加载部门存储(无论如何,您可能需要在更多地方使用它,将网格单元编辑器视为完整的部门列表)。

{
  name:'Department',
  dataIndex:'department_id',
  renderer: function(deptId){
    return Ext.data.StoreManager.lookup('Departments').getById(deptId).get('name');
  }
}

PS:我正在使用自己的非规范化进行显示,但感觉并不好;)