在ExtJS组件中如何将config: {}项转发给子组件

7

我正在尝试编写一个可重用的选项选择面板,在这个面板中,用户会看到一个网格和一个小文本框,他可以使用这个文本框来过滤网格中的内容,以便从网格中选择项目。目前,(简化后的)视图代码如下,已经能够正常工作

Ext.define('MyApp.view.items.ItemSelectorPanel', {
    extend: 'Ext.panel.Panel',
    require: 'MyApp.view.items.SimpleItemGrid',
    alias: 'widget.ItemSelectorPanel',
    layout: 'form',

    config: {
        itemStore: false
    },

    constructor: function(config) {
        this.initConfig(config);

        this.superclass.constructor.call(this, config);

        this.add([
            {
                fieldLabel: 'Filter',
                name: 'filter'
            },
            {
                xtype: 'SimpleItemGrid',
                collapsible: true,
                store: this.getItemStore()
            }
        ]);

        return this;
    }
});

如您所见,ItemSelectorPanel 使用 config 属性来公开一个接口,调用方可以指定要使用哪个项目存储。

调用方(在此示例中,面板已添加到 TabPanel):

var panelToAdd = {
    xtype: 'panel',
    title: 'New Selection',
    closable: true,
    padding: 10,
    items: [{
        title: 'Select node',
        xtype: 'ItemSelectorPanel',
        itemStore: itemStore
    }]
};

现在,我很喜欢 ExtJS 4 的声明式风格及其有助于遵循 MVC 模式的特性。我希望视图中的代码越少越好。不幸的是,这种方法行不通

Ext.define('MyApp.view.items.ItemSelectorPanel', {
    /* ... same as above ... */

    constructor: function(config) {
        this.initConfig(config);

        this.superclass.constructor.call(this, config);

        return this;
    },

    items: [
            {
                fieldLabel: 'Filter',
                name: 'filter'
            },
            {
                xtype: 'SimpleItemGrid',
                collapsible: true,
                store: this.getItemStore   // <-- interesting part
            }
    ]
});

有没有一种以声明方式通过父级属性的config 属性来暴露嵌套/子组件的配置的方法?

你可以使用 Ext.Component 的 cloneConfig() 方法来复制组件的初始配置: http://docs.sencha.com/ext-js/4-2/#!/api/Ext.Component-method-cloneConfig - blong
4个回答

6

首先是一些概述

绝不要在类定义中的函数之外添加对象,除非您确切知道自己在做什么。因为如果您这样做,所有实例都会共享该对象的相同实例。我认为我不需要提及这将导致什么问题......

如果您需要在那里放置一个对象,您应该在构造函数中克隆它。

关于您的代码

我不知道this.initConfig(config);是干什么的,但config变量不是来自于您的类,而是来自于构造函数的参数。我建议您使用initComponent()进行初始化,而不是使用constructor(),除非您有使用构造函数的明确定义需求,在您的情况下似乎没有。

此外,“config”未被转发,因为它不是从上到下执行的,而是从下到上执行,其中一个配置被传递,并且所有其他属性(已经)继承。

我仍然不确切了解您的目标,因此无法给出任何建议如何处理此问题,但我可以肯定地说,您现在的做法将导致问题。

编辑

我仍然不确定我是否完全理解您的需求,但以下操作应该有效(如果您还需要侦听器,您可以查看Ext.util.Bindable mixin)。

Ext.define('MyApp.view.items.ItemSelectorPanel', {
    extend: 'Ext.panel.Panel',
    require: 'MyApp.view.items.SimpleItemGrid',
    alias: 'widget.ItemSelectorPanel',
    layout: 'form',

    initComponent: function() {
        // Initialize the store (might be a instance or a storeId)
        var store;
        if (this.itemStore) {
            store = Ext.data.StoreManager.lookup(store);
        }
        this.itemStore = store || null;
        // Add is not valid (at least not before the parent inits are executed)
        this.items = [{
            fieldLabel: 'Filter',
            name: 'filter'
        }, {
            xtype: 'SimpleItemGrid',
            collapsible: true,
            store: this.getItemStore()
        }];

        this.callParent(arguments);
    },

    getItemStore: function() {
        return this.itemStore;
    }
});

原始提问者发布的内容是有效的,在extjs网站上有示例展示了完全相同的初始化方法。当您在类级别创建一个名为“config”的对象时,会涉及到标准的initConfig函数的特殊情况。但是,在他这里适用吗?不是的。他应该真正按照您所描述的去做。 - Reimius
@Reimius 当然没问题!但据我所知,这只发生在你定义一个“超类”的情况下。至少这是我曾经使用过的唯一情况。或者我错了吗? - sra
@sra 这里有一个链接,link,可以看一下。它们使用 cfg 构造参数(不是 config 变量,正如您所指出的),并将其放入 initConfig() 调用中。我想要实现的是将 SimpleItemGrid 的 'store' 属性呈现给调用者,使其看起来像是 ItemSelectorPanel 的属性。我希望 ItemSelectorPanel 的内部细节对外部世界隐藏。 - ErosC
@ErosC 它们都是超类,你不需要访问这些核心属性来实现你的目标。请查看我的编辑。 - sra

2
不好意思,你描述的方式是不可行的。原因很简单,我们以这个为例:
Ext.define('MyClass', {
    someProp: this.foo(),

    foo: function(){
        return bar;
    }
});

在这里,我们调用define()方法并将一个对象作为配置传递给它。因此,在传递给define之前,整个对象(包括对foo()的调用)都会被评估,因此在那时类/方法甚至不存在。
即使您可以这样做,这里还有一个复杂性,即foo是类上的实例方法,但您尝试调用它的方式就像它是静态方法一样。
因此,答案是,您需要使用某种方法来执行此操作,通常initComponent优先于构造函数。

整个配置属性处理似乎与构造函数有些联系。我尝试将其移动到initComponent中,但是出现了错误。此外,文档将其放置在构造函数中。 - ErosC

1
我没有看到回答原问题的答案。这是我发现有效的方法...
创建一个myClass实例,传递一个名为'foo'且值为'bar'的配置。
var myClassInstance = Ext.create('MyApp.view.myClass', {
    foo: 'bar'
});

myClass 的定义如下:

Ext.define('MyApp.view.myClass', {
extend: 'Ext.container.Container',
alias: 'widget.myclass',

config: {
    foo: 'defaultVal'
},

constructor: function(configs) {
    this.callParent(arguments);  //create class, calls initComponent

    var try1 = getFoo(); //try1 is 'defaultVal'
    this.initConfig(configs);  //initializes configs passed in constructor
    var try2 = getFoo(); //try2 is 'bar' 
},

initComponent: function() {
    //myClass declaration here ...

    this.callParent();
}

1
你可以在类的声明中定义项目,但不能在声明时调用任何方法。为了解决这个问题,只定义不存储项目,然后使用initComponent方法为视图设置存储。

你能给个例子吗?我尝试了以下代码:initComponent: function () { Ext.getCmp('abcd').reconfigure(this.getItemStore()); }, items: [ { fieldLabel: 'Filter', name: 'filter' }, { xtype: 'SimpleItemGrid', itemId: 'abcd', collapsible: true } ]但是提示我在未定义的对象上调用了 reconfigure() 方法。 - ErosC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接