The account view is a window that contains several subcomponents (such as login, register, and past orders). Let's take a look at the lengthy, but straightforward code for it:
// app/view/account/Account.js Ext.define('Alcohology.view.account.Account', { extend: 'Ext.Window', xtype: 'account', layout: 'fit', controller: 'account', modal: true, resizable: false, header: false, onEsc: Ext.emptyFn, width: 800, autoHeight: true, frame: true, items: [ { xtype: 'container', layout: 'column', items: [ { xtype: 'login', title: 'Login', columnWidth: 0.5 }, { xtype: 'register', title: 'Register', columnWidth: 0.5 } ], bind: { hidden: '{currentUser}' } }, { xtype: 'container', layout: 'column', items: [ { xtype: 'register', title: 'Register', columnWidth: 0.5 }, { xtype: 'panel', title: 'Past Orders', columnWidth: 0.5, items: [ { xtype: 'pastorders', bind: '{orders}' } ] } ], bind: { hidden: '{!currentUser}' } } ], bbar: [ '->', { text: 'Close', itemId: 'close' }, { text: 'Login/Register', itemId: 'loginRegister', bind: { hidden: '{currentUser}' } } ] });
We've got two panels here, both set to use a column
layout. One contains the login and registration forms and is shown when the user is logged out. Another shows the registration form repurposed as a way to let the user edit their profile details and the past orders. The second panel is only shown when the user is logged in.
The hiding and showing of components in the account window is accomplished by binding the hidden
config to currentUser
at the top-level main view model. Ext JS converts the user object to a "truthy" value, that is, either true or false. This is used to set the component's visibility.
Next, we have the login
component, which is just an Ext.FormPanel
with the relevant fields along with a little bit of explanatory text, as shown in the following code:
// app/view/account/Login.js Ext.define('Alcohology.view.account.Login', { extend: 'Ext.FormPanel', xtype: 'login', items: [ { xtype: 'fieldset', margin: 10, padding: 10, defaults: { xtype: 'textfield', width: '100%' }, items: [ { fieldLabel: 'Email', bind: '{email}', vtype: 'email' }, { fieldLabel: 'Password', inputType: 'password' } ] }, { xtype: 'container', padding: 10, html: 'If you've already got an Alcohology account,please enter your login details above. If not, please complete the registration form and join us!' } ] });
Then, we have the register
component, another form containing the fields that a user must complete in order to sign up:
// app/view/account/Register.js Ext.define('Alcohology.view.account.Register', { extend: 'Ext.FormPanel', xtype: 'register', defaultType: 'textfield', items: [ { xtype: 'fieldset', margin: 10, padding: 10, defaults: { xtype: 'textfield', width: '100%' }, items: [ { fieldLabel: 'Email', bind: '{email}', vtype: 'email' }, { fieldLabel: 'Password', inputType: 'password' } ] }, { xtype: 'fieldset', margin: 10, padding: 10, defaults: { xtype: 'textfield', width: '100%' }, items: [ { fieldLabel: 'House Number' }, { fieldLabel: 'Street' }, { fieldLabel: 'Town' }, { fieldLabel: 'County' }, { fieldLabel: 'Postcode' } ] } ] });
The final piece of the account user interface is the past orders component:
// app/view/account/PastOrders.js Ext.define('Alcohology.view.account.PastOrders', { extend: 'Ext.DataView', xtype: 'pastorders', tpl: new Ext.XTemplate('<tpl for="."><div class="past-order">', '<h3>Ordered on {date:date("m F Y")}</h3>', '<ul><tpl for="items">{name} x {quantity}</tpl></ul>', '<p>Total: £{total}</p></div></tpl>'), itemSelector: '.fake', emptyText: 'No Previous Orders.' });
Here, we use DataView with itemTpl
configured to output all of the orders as well as loop through the items within this order. As none of the past orders are clickable, there's no detail view for orders to click on to. Therefore, we need to specify a fake itemSelector
. Binding this component's store to the orders store on the view model was performed in the containing account window.
Finally, we have a simple view controller to handle interactions with the account window, as shown in the following code:
// app/view/account/AccountController.js Ext.define('Alcohology.view.account.AccountController', { extend: 'Ext.app.ViewController', alias: 'controller.account', listen: { component: { '#close': { click: 'onAccountClose' }, '#loginRegister': { click: 'onLoginRegister' } } }, onAccountClose: function(btn) { this.getView().hide(); }, onLoginRegister: function() { this.getViewModel().set('currentUser', { email: this.getViewModel().get('email') }); this.getView().hide(); } });
This is standard stuff in onAccountClose
, but in onLoginRegister
, we perform a very naïve login action in which the currentUser
gets set to an object with the e-mail address that the user entered for login or registration. As previously discussed, we're bypassing a full authentication system for simplicity, but this demonstrates the general idea, that is, perform an action that ends up with a user being set on the inherited view model. Once again, you'll see that we don't have a separate account view model as everything's passed up and down to the one that's defined on the main view.