6

I'm beginning development of an app in extjs. I'm using the MVC approach, as provided in the extjs documentation.

I have some dynamic data which needs to present the user with a set of accordion controls. I've got the data in a store, but I do not know how to dynamically create the accordion items (unlike grid panels, there doesn't appear to be a store data method).

Here is my current accordion view code - with static items:

Ext.define('BP.view.induction.LeftPage', {
extend: 'Ext.Panel',
alias : 'widget.leftpage',

title: "Left Page", 
layout: {
    type: 'accordion',
    align: 'stretch'
},
layoutConfig: {
    // layout-specific configs go here
    titleCollapse: true,
    animate: true,
    activeOnTop: true
},
items: [{
    xtype: 'panel', // fake hidden panel, so all appear collapsed
    hidden: true,
    collapsed: false
},{
    xtype: 'panel',
    title: 'Panel 1',
    html: 'Panel content!'
},{
    xtype: 'panel',
    title: 'Panel 2',
    html: 'Panel content!'
},{
    xtype: 'panel',
    title: 'Panel 3',
    html: 'Panel content!'
}]
});

Any guidance on how to achieve the above would be appreciated, thank you.

[Edit] In response to sra's request, here is my controller:

Ext.define('BP.controller.Induction', {
extend: 'Ext.app.Controller',

views: [
    'induction.Binder'
],

stores: [
    'Sections',
    'Categories',
    'Tasks'
],

init: function() {
    console.log('Initialized Induction!');
}
});

I should note here that this controller loads a parent view, which in turn loads the LeftPage view - I'm not sure if this creates any scoping issues. Furthermore, as you can see, more than one store is loaded.

BrynJ
  • 8,322
  • 14
  • 65
  • 89

1 Answers1

7

You can do like this (untested example with some tweaks)

Ext.define('BP.view.induction.LeftPage', {
    extend: 'Ext.Panel',
    alias : 'widget.leftpage',

    title: "Left Page", 
    layout: null,
    layoutConfig: null,
    store: null,
    attentive: true,
    initComponent: function() {
        var me = this;
        // begin edit
        // only set the store if is is not already defined
        me.store = me.store ? me.store : Ext.StoreMgr.lookup('Sections');  // you may change this to any storename you want
        // end edit
        me.layout = { // don't set objects directly in class definitions
            type: 'accordion',
            align: 'stretch'
        };
        me.layoutConfig = { // dont set objects directly in class definitions
            titleCollapse: true,
            animate: true,
            activeOnTop: true
        };

        me.callParent(arguments);
        if (me.attentive) {
            me.store('load', me.onRebuildContent, me);
            if (me.store.count() == 0)
                me.store.load();
        } else {
            me.buildContent(); 
        }
    },
    buildContent: function() {
        var me = this;
        function addItem(rec) {
            me.add({
                xtype: 'panel',
                title: rec.get('titleProperty'),
                html: rec.get('bodyProprty')
            });
        };

        me.store.each(addItem);
    },
    onRebuildContent: function() {
        var me = this;
        me.removeAll();
        me.buildContent();
    }   
});

Your store will need at least two properties; one for the title and one for the content. And the store need to be loaded before you should instantiate this. But that can easily be done within your controller.

Edit based on comment and new OP info:

Well your view is a bit out of control of the controller. So I recommend you to simply use the StoreManager to receive a valid instance (I've edited the code, I just didn'T know the correct store to use). The StoreManager will know about the store as long as you list him within a controller (StoreManager is capable of much more, but that is all you need to know at the moment). For a further release you could also use the mixin bindable which would manage a storebinding more clean and enables you to update your accordion after the store receives new data (get updated)

Edit for readability

I've just cleaned it up a bit and included a switch param attentive which would allow you to use this component as directly bound to a store, reacting on all load events or as a sort of static one where the store should already be loaded. All in all this should give you a start without making it to complex.

sra
  • 23,820
  • 7
  • 55
  • 89
  • Thanks for the help sra. I've added the code and changed this line: `me.sections.each(addItem);` and I'm loading the store using 'stores' parameter in the controller, but it's telling me that it is undefined. I'm sure this is something quite obvious, but as I'm a extjs newbie I'm still struggling with the basics. Any assistance you can offer would be greatly appreciated. – BrynJ Nov 12 '12 at 11:48
  • @BrynJ Well, I guess it is straight forward from there on; create instance of `'BP.view.induction.LeftPage'` using `Ext.create('BP.view.induction.LeftPage', { store: yourStoreVariable });` instead of `Ext.widget()`. If this not help please drop the controller function where you instantiate this class and I will see what I can do. – sra Nov 12 '12 at 11:58
  • I'm sure it is obvious, but not to me :) I have added my controller code to the question and also some more details below it. – BrynJ Nov 12 '12 at 12:11
  • You'll need to make a constructor aswell, where you'll pass the store to your object. And you need to make sure your store is loaded before u call buildContent, it might be better to listen to the load event of the store. initComponent will have something like: me.store.on('load', function() { me.buildContent() }); – Johan Haest Nov 12 '12 at 12:15
  • Absolutely! But this could also be done by a controller. Anyway I cleaned up my example. – sra Nov 12 '12 at 13:06
  • Thank @sra. It is working great now, although your update produces an 'attentive is undefined' error. I have one question though - and perhaps more that will require another SO question - how would I filter the data returned by the store? Looking at the documentation, I see there is a filter method but this returns null when run on the store? – BrynJ Nov 12 '12 at 14:21
  • @BrynJ I recommend you to apply `remoteFilter: true` to the store you need to filter. With that you can only filter for params + value. The Filters are packed automatically with each request. At the server you just need to read the list named filter which contains object like { property: 'YurUsedString', value: 'YourUsedValue' }. It is now up to you to apply these filters before returning the request. Local Filters are normally regex or functions . They create a copy of the data while the filter is active. A filter filters the store and has therefor no return value, he just get applied. hth – sra Nov 12 '12 at 14:29
  • @BrynJ Ah yes, and I fixed the `attentive` error. I missed the access modifier – sra Nov 12 '12 at 15:09