Do we need it?
Perhaps not. For small applications that have a couple of views, one container with one “main” view model that is holding data, a few stores and a couple of formulas for needs of each and every view we do not need any hierarchy of view models.
But imagine an application with one or two hundreds of views. Do we still want to keep all data and stores in the main view model and let its code grow to thousands, many thousands of lines? Initializing stores, formulas and data also when the view using them is not currently visible or instantiated?
The easy solution then, right? Let’s put all “view-only” stores, data and formulas in their own view models that have lifetime identical to the lifetime of their views.
Well, are almost there. Not quite, but almost. The problem we have now is how to bind data between view models. For example, selecting a row in a grid should populate a form with detail data. But both grid and form have their own view models. We would like to publish the grid selection up to the main view model to which we could bind the form fields, yet we would like to keep the grid store configuration in the “local” grid’s view model.
When we need it, how to implement it?
To demonstrate, let take the following simple example.
Selecting a row of the grid causes that the underlying record from the grid store populates the detail form on the right.
Right after running sencha generate view ...
we have this structure:
There are no relations between views and their view models other than that views use their view models. It is just a starting point for us to start configuring data binding and publishing.
The first thing to configure in the grid view model is the store. And, because we want the grid to publish its selection, we set a “reference” in the grid view. If a reference for the grid is configured, the selected record is automatically published.
Whereto is it published? To the grid’s view model. The missing ingredient is how to get the selected record up to the main view model so that we could bind the form fields.
For that we need to do two things:- In the main view model, configure the object that will hold the currently selected customer:
data: { // We need an object here as view models use prototype chain // so the 'current' object is inherited by lower view models // We bind to {current.customer} throughout the application current:{ customer:null } } // eo data
-
In the grid view model, define the formula that does the trick:
formulas:{ // current customer is bound to automatically published selection currentCustomer:{ bind:'{customersList.selection}' ,get:function(customer) { // this is core of the trick // whenever selection changes, this method is called // so we also update current.customer that is defined // in the main view model this.set('current.customer', customer); return customer; } // eo function get (currentCustomer) } // eo currentCustomer } // eo formulas
The rest is peanuts, we just bind the form fields to “{current.customer.[field name]}” and we are done. Of course, we can utilize the form view model to calculate some derived data for us, such as valid and dirty states.
I believe that this technique helps you to code better, robust and modularized application without a spaghetti code.
- Ext, Angular, React, and Vue - 27. June 2019
- The Site Resurgence - 11. February 2018
- Configuring ViewModel Hierarchy - 19. June 2015
6 Responses
My get handler for the formula never gets fired. I am binding to the panel width property using reference? What am i doing wrong? Am i missing something obvious?
I did love your article,butI have some different vision in this part,could you give some advice,please.
http://cheneycode.blogspot.tw/2015/12/extjs-mvvm-design-pattern.html
It is the default behavior of the grid. If grid has a reference it automatically publishes selection to the nearest ViewModel.
Hi Saki,
I have a question why Selected record is automatically published to View model? How just by specifying reference selected record are published to view model.
Please let me know the mechanism behind this
Thanks
Thanks for the explanation. It seem this pattern is susceptible to automation so hopefully future releases will make this simpler or, at least, obvious.
Bill
Right. Until then we have this method to work with. I’ve used it many times in big apps and it works fine.