Card layout question

45 views
Skip to first unread message

murrayh...@gmail.com

unread,
Jun 28, 2015, 5:50:59 AM6/28/15
to gl...@googlegroups.com
I am very new to GluJS and I am learning a lot quickly. I am very impressed so far!

I have a question about the GluJS equivalent of ExtJS this.getLayout().setActiveItem(cardIndex) which is used to dynamically change the active card in a panel with a card layout. I have a solution that works but think there is probably a better way.

On my viewPort I have a menu in the west panel with 3 menu items and a panel in the centre with a card layout. When the user selects a menu item the matching card in the centre panel is made active (eg Click the "Users" menu item to view the Users components in the centre panel). Here is my solution (just the relevant bits from my simple test app).

I have defined the menu items using the convention binding (amazing functionality BTW):
items : ['btnWelcome','btnUsers','btnInvoices']

/* The Menu viewModel */
glu.defModel('cpanel.VPMainMenu', {

// Initialise to 'Welcome'
selectedItem: 'Welcome',

btnWelcome: function() {
this.set('selectedItem', 'Welcome');
},
btnUsers: function() {
this.set('selectedItem', 'Users');
},
btnInvoices: function() {
this.set('selectedItem', 'Invoices')
},

onSelectedItemChange: {
on: ['selectedItemChanged'],
action: function(newValue, oldValue, obj) {

// The selected item property has changed.
// Tell the view model for the VPCentreComponent to change
// to the matching card
// Call the "exposed" method on the centre viewmodel:
this.parentVM.vmVPCentreComponent.mainMenuButtonChange(newValue, oldValue, obj);
// Is it possible for the vmVPCentreComponent to "listen" for the
// selectedItemChange on THIS viewModel??
}
}
});

The centre viewmodel:

glu.defModel('cpanel.VPCentreComponent', {

selectedCardName: 'Welcome',

mainMenuButtonChange: function(newValue, oldValue, obj) {
// The corresponding view for this viewModel (vVPCentreComponent)
// has a binding to the selectedCardName property above
this.set('selectedCardName', newValue);
}
});

The centre view:

glu.defView('cpanel.VPCentreComponent', {

layout: 'card',

// Binding to the viewModel 'selectedCardName' property
activeCardName: '@{selectedCardName}',

// Listen for when the activeCardName property changes,
// find the relevant card by name (itemId) then
// make that the active card in the panel.
setActiveCardName: function(cardName) {
// When we get here, 'this' scope is Ext.panel.Panel so the normal
// ExtJS methods can be used

var cardIndex = this.items.findIndexBy(function(item) {
return (item.itemId === cardName)
});

if (cardIndex > -1) {
this.getLayout().setActiveItem(cardIndex);
} else {
throw ('No matching card panel for item ' + cardName)
}
},

// In the real app these cards will have nested viewModels
items: [{
html: 'Welcome to the Control Panel',
itemId: 'Welcome'
}, {
html: 'Work with user records and review logs',
itemId: 'Users'
}, {
html: 'Check invoices and payments',
itemId: 'Invoices'
}]
});

Sorry for the amount of code, I stripped out as much as I could.

So, there are two questions:
1. Is there a better way to achieve what I want? ie ultimately to setActiveItem(cardIndex)

2. Is it possible for one viewModel to "listen" to a property change on another viewModel? (eg via a Reactor property)

Thank you!
Cheers,
Murray

murrayh...@gmail.com

unread,
Jun 28, 2015, 8:15:42 PM6/28/15
to gl...@googlegroups.com, murrayh...@gmail.com
I think my question 2 is already answered here: https://groups.google.com/d/topic/glujs/m3iNUPLzkuw/discussion

I am still digesting it.
Message has been deleted

Ryan Smith

unread,
Jun 29, 2015, 12:44:05 PM6/29/15
to gl...@googlegroups.com, murrayh...@gmail.com
Hey Murray,

I think what you're doing is on the right path... although we've already built something like that into GluJS because its a pattern that we've encountered too many times :).  The thing you're looking for is called "activatorlist" and it works like this:

mtype: 'activatorlist',
focusProperty: 'propertyToLookAtInViewmodelForChangingWhichCardIsShown'

The way it works is that you create a list of viewmodels that you want to be displayed, and you tell the activatorList which one is going to be active by setting the focus property in your viewmodel.  Then when someone changes the menu, clicks a tab, clicks a button, clicks something like next, whatever... you can just set the focusProperty in your viewmodel.  This will fire the event for the activatorlist which will set the appropriate viewmodel to be active and shown.  This is a pattern we use all the time (tabs, wizards, master/detail with different types of data).

I believe that answers both 1 and 2 from your questions, but let me know if I can clear things up for you even more.  Have a great day!

-Ryan

On Sun, Jun 28, 2015 at 5:26 PM <murrayh...@gmail.com> wrote:
I am very new to GluJS and I am learning a lot quickly. I am very impressed so far!

I have a question about the GluJS equivalent of ExtJS this.getLayout().setActiveItem(cardIndex) which is used to dynamically change the active card in a panel with a card layout. I have a solution that works but think there is probably a better way.

On my viewPort I have a menu in the west panel with 3 menu items and a panel in the centre with a card layout. When the user selects a menu item the matching card in the centre panel is made active (eg Click the "Users" menu item to view the Users components in the centre panel). Here is my solution (just the relevant bits from my simple test app).

I have defined the menu items using the convention binding (amazing functionality BTW):
<code language="javascript">

items : ['btnWelcome','btnUsers','btnInvoices']
</code>
<code language="javascript">

/* The Menu viewModel */
glu.defModel('cpanel.VPMainMenu', {

    // Initialise to 'Welcome'
    selectedItem: 'Welcome',

    btnWelcome: function() {
        this.set('selectedItem', 'Welcome');
    },
    btnUsers: function() {
        this.set('selectedItem', 'Users');
    },
    btnInvoices: function() {
        this.set('selectedItem', 'Invoices')
    },

    onSelectedItemChange: {
        on: ['selectedItemChanged'],
        action: function(newValue, oldValue, obj) {

            // The selected item property has changed.
            // Tell the view model for the VPCentreComponent to change
            // to the matching card
            // Call the "exposed" method on the centre viewmodel:
            this.parentVM.vmVPCentreComponent.mainMenuButtonChange(newValue, oldValue, obj);
            // Is it possible for the vmVPCentreComponent to "listen" for the
            // selectedItemChange on THIS viewModel??
        }
    }
});
</code>
The centre viewmodel:
<code language="javascript">

glu.defModel('cpanel.VPCentreComponent', {

    selectedCardName: 'Welcome',

    mainMenuButtonChange: function(newValue, oldValue, obj) {
        // The corresponding view for this viewModel (vVPCentreComponent)
        // has a binding to the selectedCardName property above
        this.set('selectedCardName', newValue);
    }
});
</code>
The centre view:
<code language="javascript">
</code>

Sorry for the amount of code, I stripped out as much as I could.

So, there are two questions:
1. Is there a better way to achieve what I want? ie ultimately to setActiveItem(cardIndex)

2. Is it possible for one viewModel to "listen" to a property change on another viewModel? (eg via a Reactor property)

Thank you!
Cheers,
Murray

--
You received this message because you are subscribed to the Google Groups "GluJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to glujs+un...@googlegroups.com.
To post to this group, send email to gl...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/glujs/956b86eb-1e48-4f5b-8b22-b5656db06872%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

murrayh...@gmail.com

unread,
Jun 29, 2015, 8:10:51 PM6/29/15
to gl...@googlegroups.com, murrayh...@gmail.com
Hi Ryan,

Thanks for your reply. I thought you must have a solution but it wasn't in the Guide or the API docs so I didn't know about it.

Looking at the Glu code I see that the activatorlist is a descendant of list, which makes sense. I have tried to implement but haven't been successful. I need a bit more info.

I have 3 viewModels (DashboardCmp, UsersCmp and InvoicesCmp) and the main viewModel (VPCentreComponent) that hosts the activator list.

>The way it works is that you create a list of viewmodels that you want to be displayed
So, I tried various ways of adding the list that you refer to and the closest I get so that the activatorlist inits the sub models is:
glu.defModel('cpanel.VPCentreComponent', {

cmpList: {
mtype: 'activatorlist',
focusProperty: 'alSelection', // propertyToLookAtInViewmodelForChangingWhichCardIsShown
items: [{
mtype: 'DashboardCmp'
}, {
mtype: 'UsersCmp'
}, {
mtype: 'InvoicesCmp'
}]
},
alSelection : 0,
mainMenuButtonChange: function(newValue, oldValue, obj) {
// For now hard code the button click to load the Invoices component
this.set('alSelection', 2);
}
});

The view for the VPCentreComponent looks like this:

glu.defView('cpanel.VPCentreComponent', {
layout: 'card',
items: '@{cmpList}'
});

When I click a button on the main menu it fires the mainMenuButtonChange() and does fire the alSelectionChanged listener which in turn calls setActiveIndex(2) but the panel doesnt display (and no error is thrown).

What am I missing here?

Thanks,
Murray

Ryan Smith

unread,
Jun 29, 2015, 10:12:05 PM6/29/15
to gl...@googlegroups.com, murrayh...@gmail.com
Hey Murray,

Based on what you sent, everything should be working.  The only thing I can think is that the DashboardCmp, UsersCmp, and/or InvoicesCmp isn't properly defined?  I would imagine you'd see some kind of an error somewhere along the way though if that was the case.

One thing you can try is to make sure that your view has the "activeItem" bound to your viewmodel focus property.  activeItem: '@{alSelection}' which will setup the extjs code to be properly bound as well.  That might kick the tires and get you going.

If that doesn't work... feel free to zip it up and send me an example and I'd be happy to look at it for you and see what I can do to fix it up.

Good luck!

-Ryan

--
You received this message because you are subscribed to the Google Groups "GluJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to glujs+un...@googlegroups.com.
To post to this group, send email to gl...@googlegroups.com.

murrayh...@gmail.com

unread,
Jun 29, 2015, 11:34:50 PM6/29/15
to gl...@googlegroups.com, murrayh...@gmail.com
Thanks! Yes I needed the activeItem: '@{alSelection}' to make it work. I will post the pattern soon - after I have digested the implications of it!

murrayh...@gmail.com

unread,
Jun 30, 2015, 1:03:54 AM6/30/15
to gl...@googlegroups.com, murrayh...@gmail.com
OK. This is the pattern that worked.
Main viewModel with the activatorlist:

glu.defModel('cpanel.VPCentreComponent', {

cmpList: {
mtype: 'activatorlist',
focusProperty: 'selectedCmp',
items: [{
mtype: 'DashboardCmp'
}, {
mtype: 'UsersCmp'
}, {
mtype: 'InvoicesCmp'
}]
},

selectedCmp: 0,

mainMenuButtonChange: function(newValue, oldValue, obj) {
// Use the key passed from the main menu item (newValue) to
// lookup the matching component (using it's viewmodelName for now).
// Return the matching component and set it to the selectedCmp property
// which will fire the relevant change event and make the matching
// viewModel (and it's associated view) the active one.

var cmp = this.cmpList.find(function(vm) {
return (vm.viewmodelName === newValue + "Cmp")
});

if (cmp) {
this.set('selectedCmp', cmp);
} else {
throw 'No matching view model for menu item key=' + newValue
}
}
});

It's view:
glu.defView('cpanel.VPCentreComponent', {
layout: 'card',
activeItem: '@{selectedCmp}',
items: '@{cmpList}'
});

With this the Dashboard panel, Users panel and Invoices panel becomes active as the user clicks the menu. All good. Not only that, amazingly simple implementation! Very cool.

Now, this might be obvious, but what is the best way for a viewModel to know it was just made the active one? I've tried various events and looked at the code but cant find something that works.

The reason I need it is that I don't want to load data from a server when the UsersCmp viewModel init() runs, I want to load it only when the user makes that viewModel active. They might never look at it and there is no reason to load the data if they don't need it.

Thanks,
Murray
Reply all
Reply to author
Forward
0 new messages