the singularity of being and nothingness
ExtJS 4.2 Walkthrough — Part 11: Executive Dashboard
With the successful roll-out of our workflow system, management has come up with another great idea: an Executive Dashboard. The main idea is that when anyone with the “Admin” role logs in, they should be greeted with a “dashboard” of sorts that will allow them to see a variety of details from across the application, such as snapshots of the charts we previously built, outstanding workflow actions, and the most recently added inventory records.
Of course, we’ve already built all this stuff, so we confidently respond that this is no problem, and we’ll have it done in a jiffy. So let’s get jiffy-ing!
NOTE: Code for this installment can be found on GitHub.
Strategy
One of the best things about the way we’ve built our ExtJS 4 app is that we can now really start to benefit from reuse of code. Instead of having to re-create all the components (charts, grids, etc), we can simply instantiate them in new contexts and move on with our lives.
For this Executive Dashboard, we’ll reuse both of the charts that we created earlier, as we all reusing the inventory grid twice. Since we’ve already created stores and models for all of these, there’s ZERO that we have to create in this regard. Hooray!
To organize things, we’ll utilize the Ext.layout.container.Border layout as well as the Ext.layout.container.Anchor layout to create a nice “quadrant” effect. Ultimately, we want something like this:
The Dashboard
Our dashboard view (view/executive/Dashboard.js) is very straightforward. Since the components we want to inject into our dashboard already exist, we simply need to instantiate them via xtype…just like we’ve been doing all along:
Ext.define('CarTracker.view.executive.Dashboard', { extend: 'Ext.panel.Panel', alias: 'widget.executive.dashboard', layout: 'border', initComponent: function() { var me = this; Ext.applyIf(me, { items: [ // column 1 { xtype: 'container', region: 'west', width: '50%', layout: 'anchor', split: true, items: [ { xtype: 'panel', title: 'Monthly Sales', iconCls: 'icon_barchart', anchor: '100% 50%', layout: 'fit', items: [ { xtype: 'report.month.chart', store: { type: 'report.month' } } ] }, { xtype: 'car.list', dockedItems: false, title: 'Latest Additions', anchor: '100% 50%', store: { type: 'car', pageSize: 6, sorters: [ { property: 'CreatedDate', direction: 'DESC' } ] } } ] }, // column 2 { xtype: 'container', region: 'center', width: '50%', layout: 'anchor', items: [ { xtype: 'car.list', dockedItems: false, anchor: '100% 50%', title: 'Audits Awaiting Approval', iconCls: 'icon_workflow', viewConfig: { deferEmptyText: false, emptyText: 'No audits awaiting approval!', markDirty: false }, store: { type: 'car', remoteFilter: true, pageSize: 6, sorters: [ { property: 'CreatedDate', direction: 'DESC' } ], listeners: { beforeload: function( store, operation, eOpts ) { store.getProxy().extraParams = { filter: Ext.encode([ { property: 'Status', value: [3] } ]) }; } } } }, { xtype: 'panel', title: 'Sales by Model', iconCls: 'icon_piechart', anchor: '100% 50%', layout: 'fit', items: [ { xtype: 'report.make.chart', title: 'Sales by Make', store: { type: 'report.make' } } ] } ] } ], }); me.callParent( arguments ); } });
This looks pretty much like any of our other views. However, I will point out a few things highlighted in bold.
First, for each of our border layout’s regions, we create containers that have an anchor layout. The Ext.layout.container.Anchor allows us to “anchor” child items to positions relative to the parent. In this case, we define that each child item should occupy 100% width and 50% height of the containing element. And if we ever resize, our child items will resize along with the parent.
Next, you’ll notice that for both of our charts, we override the store that they should use at instantiation. If you remember, our actual chart components have inline, non-proxy-ed stores that are loaded with data from their sibling grids. Obviously, since the grids are not available on the dashboard, we need to get that data elsewhere. While we could refactor our charts, we can also simply override the settings as needed. In this case, that’s what we’ll do.
Finally, and related, you’ll notice that we’re doing something similar on our inventory grids. Again, we could refactor our original approach entirely, or even create new custom components; but passing new configs is part of the beauty of the ExtJS 4 component architecture, so taking advantage of it is what will help us fully realize the power of component reuse.
Of course, the best part of all of this is that because we are reusing these components, all of the functionality that we’ve bound to them (e.g., the inventory grid’s contextmenu events, etc.) are all still active. So our Admin role users can effectively manage their portion of the application from the dashboard without having to navigate to the “official” section of the app. Do I sense a bonus in our future?
A New Controller
While we don’t technically need it, let’s go ahead and create a new Dashboard.js controller. Executive Dashboards have a funny way of growing in complexity very quickly, so by creating a separate controller we are merely future-proofing ourselves against the inevitable.
This new controller is going to be very thin–in fact, the only real work that it will do is to define a beforerender listener for our dashboard component, and call a method which will handle loading any data that we need for our dashboard view’s components:
/** * Controller for Executive Dashboard functionality */ Ext.define('CarTracker.controller.Dashboard', { extend: 'CarTracker.controller.Base', stores: [ 'report.Makes', 'report.Months' ], views: [ 'executive.Dashboard', 'report.make.Chart', 'report.month.Chart', 'car.List' ], init: function() { this.listen({ controller: {}, component: { '[xtype=executive.dashboard]': { beforerender: this.loadDashboards, } }, global: {}, store: {}, proxy: {} }); }, /** * Handles initial loading of the executive dashboard * @param {Ext.panel.Panel} panel * @param {Object} eOpts */ loadDashboards: function( panel, eOpts ) { var me = this, makereport = panel.down( '[xtype=report.make.chart]' ), monthreport= panel.down( '[xtype=report.month.chart]' ); // call report stores manually makereport.getStore().load({ params: { filter: Ext.encode([ { property: 'SalesByMake', value: true }, { property: 'IsSold', value: true } ]) } }); monthreport.getStore().load({ params: { filter: Ext.encode([ { property: 'SalesByMonth', value: true }, { property: 'IsSold', value: true } ]) } }); } });
NOTE: An important thing to be aware of is that we’ve already bound a beforerender event in our Cars.js controller to the inventory grid component. Because ExtJS controller listeners fire across the entire application, the loadRecords() method defined in Cars.js will be run when we render the inventory grid components on the dashboard.
While this is desirable behavior in our scenario (e.g., we want data to be loaded on render), it can also create conflicts if we are not careful. This is one very good reason to be as specific in your component selectors as possible.
Nothing to it. With that, everything is working. The last bit is to add a new menu item (restricted to only Admin roles), and update our App.js to handle the history event:
view/layout/Menu.js:
{ text: 'Executive Dashboard', itemId: 'dashboard', iconCls: 'icon_dashboard', hidden: !CarTracker.LoggedInUser.inRole( 1 ) }
controller/App.js:
dispatch: function( token ) { ... switch( token ) { ... case 'dashboard': allowedRole = 1; config = { xtype: 'executive.dashboard' }; break; ... } ... }
Wrapping Up
That was cake, right? We had to do almost ZERO extra development in order to pull this off, and because of the inherent shininess of dashboards we’ll probably get more kudos from the check-writers than from anything that took us hours of sweat and tears to implement.
Print article | This entry was posted by existdissolve on July 14, 2013 at 9:48 am, and is filed under ExtJS, ExtJS 4.2. App Walkthrough, JavaScript. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |
about 10 years ago
Many wonderful thanks to you for your thorough analysis and real examples with extjs 4.x. It is a great work of research and deep understanding of the API. It is more than some textbooks written about extjs.
However, please as a request, I need a brief comprehensible tutorials on the following (in extjs 4.x):
1. Using TaskManagement to schedule automatic form submission per 60seconds consecutively for 10times.
2. How to show the actual file image on the screen before the file is submitted to the server
3. How to change from one language to another (e.g. from english to french) when a button is clicked, or automatically when the application url is opened in the browser (using extjs 4.x)
4. How to view PDF file in extjs
Many thanks in advance.
about 10 years ago
@Mfon–
Thanks for the kind words! I’m very happy that this walkthrough has been beneficial to you!
1.) I would start by breaking this down to a simpler example. Leave out the form submission for the time being, and concentrate on the scheduled task part. Once you have that working, you can easily add in the form submission part afterward. Here’s a simple example of your 10-step repeating task: https://fiddle.sencha.com/#fiddle/1pb
2.) This is really a question that relates to the new HTML 5 File API. It can be integrated easily enough into Ext JS, but isn’t technically an Ext JS issue. I’ve put together a simple example of how to accomplish what you want with the HTML 5 File API in conjunction with Ext JS: https://fiddle.sencha.com/#fiddle/1pd. Please note, of course, that the HTML 5 File API is not supported in every browser, so to use this for a real application, you’ll want to put processes in place to check that the API support exists and if it doesn’t, provide some kind of fall back (if possible).
3.) The internationalization question is a big, complicated one and can be accomplished in a number of ways (each of which have their supporters and detractors). I’d suggest starting with this thread (http://www.sencha.com/forum/showthread.php?81925-ExtJS-Internationalization) and going from there.
4.) The easiest way to do this is via an iFrame. You can use either the http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.ux.IFrame, or just use the autoEl{} config on a component to create a iFrame whose SRC attribute points to the desired PDF. Here’s a simple example of that: https://fiddle.sencha.com/#fiddle/1pe
Hope that helps!
about 10 years ago
Thanks for your tutorial. It is awesome. Do you have a link to the working version of this tutorial?
Also i was wondering on a button action on a grid how would you load another view on the same area (meaning, you are in the Car List view & selecting a row on the grid (Car), you show the Car Details view on the same area passing context)
Thanks
about 10 years ago
Many many thanks once again in response to my questions.
about 10 years ago
Please one again, i have some questions concerning html5 and extjs4.x or above.
1. How do i use extjs4.x or above for programming webGL? Also i do i incorporate another javascript api (three.js) into extjs4.x since extjs has a unique namespace for application.
2. How do i use Extjs4.x or above for viewing, capturing and sending image from webcam.
4. can extjs 4.x or above be used to access my system usb devices e.g. usb modem or usb camera or biometric thumb printing device? if yes how?
5. how do i use extjs4.x or above for canvas scalable vector graphics (svg).
thanks a million times in advance.
about 10 years ago
Hi Mfon-
1.) If I’m not mistaken, you will need to include a 3rd-party library for webGL. However, you can easily integrate any library with ExtJS–just create a class that does the necessary instantiation of the 3rd-party tools. Then you can either interact with the library natively, or turn your class into an API for it.
2.) You’ll have to use the HTML5 apis (browser-dependent) to do this. There’s nothing natively in ExtJS that will do it out of the box, but you could easily create a wrapper API for the HTML5 interfaces.
3.) As with #2, there’s nothing in Ext JS that will do this out of the box. However, if there are other APIs that are available for these things, you can easily integrate them.
4.) For SVG, check out the drawing package: http://docs.sencha.com/extjs/4.2.1/#!/guide/drawing
about 10 years ago
When i start to execute the car tracker parteleven it says “Sorry, an error occurred during your request. Please try again.”
How can i fix this?
Thanks
about 10 years ago
Hi kat–
That message occurs when there’s an AJAX request failure on the doLogin() method, typically because of a server-side error. Have you checked your logs or the AJAX response for the error?
Thanks!
about 10 years ago
wonderfull tutorial, thanks!
what about extjs5 i am trying to refactoring this code to extjs5 and i am not sure about all you do, for example you use base controller with load detail function, but why?
you can not use model loading api?
about 10 years ago
Hi Roman–
No necessity to use the loadDetail() approach…it’s just the approach I had adopted back when I developed this walkthrough. If I had it to do over, I’d probably use the model API.
about 10 years ago
Also, I know it doesn’t do any good at this particular moment, but I hope to re-do this demo app as a walkthrough for Ext JS 5 in the coming months. Thanks!
about 10 years ago
Hi.
Thanks for your wonderful example 🙂
Is it possible to save the Dashboard in ExtJS?
about 10 years ago
Hi Suraj–
What do you mean by save? Are you talking about downloading it as an image?
about 10 years ago
Not as image. That can be done. I am rather saying to save the dashboard. Obviously for that i have user configuration i.e. user is logged in to my application and have created his dashboard.
Now suppose user create the dashboard for “Account” and next he wants to create another dashboard for “Sales”. So in such case user must be able to Save his Account dashboard, so that later after logged in he may open his Account dashboard again with same layout and widgets.
Thanks for your reply 🙂
about 10 years ago
hi, I like your work.
Is it possible to have the script for the database.
thank you
about 10 years ago
Try this out: https://github.com/existdissolve/Pink-Unicorns-Do-Exist/blob/master/CarTracker.sql
about 10 years ago
After reading this great walkthrough i finally could see how to put together a mutiuser platform for desktop-like office applications based on node.js/node-webkit as server/brain backend.
There are app modules for one particular task or functionality. For example there are CarTracker and MVC FeedViewer modules.
https://github.com/olecom/supro-demo
Big thanks!