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

12,505

Solution 1

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

Solution 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.

Solution 3

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.

Solution 4

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();
}
Share:
12,505
ErosC
Author by

ErosC

Updated on June 12, 2022

Comments

  • ErosC
    ErosC almost 2 years

    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?

  • Reimius
    Reimius over 11 years
    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.
  • sra
    sra over 11 years
    @Reimius 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?
  • ErosC
    ErosC over 11 years
    @sra 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.
  • ErosC
    ErosC over 11 years
    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.
  • ErosC
    ErosC over 11 years
    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.
  • sra
    sra over 11 years
    @ErosC They are both superclasses and you will not need to access these core properties to archive your goal. See my edit