In ExtJS components how to forward config: {} items to sub components
Asked Answered
F

4

7

I am trying to write a reusable item selection panel where the user has a grid with items he can choose from and a small text field that he can use to filter the content of the grid. Right now the (simplified) view code looks like this and works.

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;
    }
});

As you can see the ItemSelectorPanel uses the config property to expose an interface where the calling site can specify which item store to use.

Calling site (in this case the panel is added to a TabPanel):

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

Now, I love the declarative style of ExtJS 4 and how it helps to follow the MVC pattern. I would prefer to have the least amount of code possible in the views. Unfortunately this does not work:

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
            }
    ]
});

Is there a way to expose the config of a nested/sub component via the config property of the parent property in a declarative manner?

Fig answered 17/1, 2013 at 17:43 Comment(1)
You can copy the initial config of a component using Ext.Component's cloneConfig() : docs.sencha.com/ext-js/4-2/#!/api/…Hamo
E
6

First something in general

Never add an object outside a function within a class definition unless you exactly know what you are going to do. Cause if you do so all instances will share the same instance of that object. I think I do not need to mention where this leads to...

If you have a need to place a object there you should clone it within the constructor.

To your code

I dunno what this.initConfig(config); does but the config variable is not the one from your class, it is the one from the constructor argument. I recommend you also to use initComponent() for initialization instead of the constructor() unless you have a defined need for using the constructor, which in your case you don't seem to have.

Also a 'config' is not forwarded cause it don't get executed up->bottom but bottom->up where a config get's hand up and all other properties are (already) inherited.

I still do not exactly know what your goal is, therefore I cannot give you any advice how you should do this but I can say for sure that the way you do it will lead to problems.

Edit

I still not sure that I have fully understand your needs but the following should work (if you need the listeners too you might take a look at the 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;
    }
});
Evelunn answered 18/1, 2013 at 10:49 Comment(4)
What the original asker has posted is valid, there are examples on the extjs website showing the exact same method of initialization. There is a special case when you create an object called 'config' at the class level that involves a standard initConfig function. Is it appropriate for his situation here though..? no. He should really be doing what you describe.Tonnie
@Tonnie Absolutely! But as far as I know that is only when you define a 'superclass'. At least that is the only situations where I have ever used this. Or am I wrong?Evelunn
@Evelunn Have a look here link. They take the cfg constructor parameter (not the config variable, as you also pointed out) and put it into the initConfig() call. What I am trying to achieve is to present the 'store' property of the SimpleItemGrid to the caller such that it looks as if it was a property of the ItemSelectorPanel. I want the internal details of the ItemSelectorPanel to be hidden from the outside world.Fig
@Fig They are both superclasses and you will not need to access these core properties to archive your goal. See my editEvelunn
L
2

No, you can't do it in the way you've described. The reason is pretty simple, let's take this as an example:

Ext.define('MyClass', {
    someProp: this.foo(),

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

Here, we call the define() method and we pass it an object as the configuration. As such, the whole object (including the call to foo()) is evaluated before it's even passed to define, so the class/method doesn't even exist at that point.

Even if you could do that, here's also the complication that foo is an instance method on the class, but the way you're attempting to call it is as though it's a static method.

So, the answer is, you'll need to use some kind of method to do so, initComponent is typically preferred over the constructor.

Lichtenfeld answered 18/1, 2013 at 10:59 Comment(1)
The whole config property handling seems to be somehow tied to the constructor. I tried moving it to initComponent but I got errors. Also the documentation puts it into the constructor.Fig
R
1

You can define items in declaration of your class but you cannot call any method from your class at time of declaration. To solve it, define only items without store and than use initComponent method to set store for your view.

Rogovy answered 18/1, 2013 at 11:9 Comment(1)
Can you show an example for that? I tried ` initComponent: function () { Ext.getCmp('abcd').reconfigure(this.getItemStore()); }, items: [ { fieldLabel: 'Filter', name: 'filter' }, { xtype: 'SimpleItemGrid', itemId: 'abcd', collapsible: true } ] ` Tells me that I am calling reconfigure() on undefined.Fig
E
1

I didn't see an answer that addressed the original question. Here is what I've found to work ...

Creating an instance of myClass, passing in a config 'foo' with value 'bar'

var myClassInstance = Ext.create('MyApp.view.myClass', {
    foo: 'bar'
});

myClass is defined as follows :

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();
}
Erinn answered 10/8, 2013 at 23:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.