Creating a two-way binding between View elements and JavaScript objects using the Model View ViewModel (MVVM) pattern
There are various architectural patterns that are used in most of the applications. The three very important architectural patterns are as follows:
Model View Controller (MVC)
Model View Presenter (MVP)
Model View ViewModel (MVVM)
These patterns are heavily used in structuring both the desktop and server-side applications. In recent years, these patterns have been applied to client-side applications as well. Frameworks such as AngularJS, BackboneJS, and EmberJS provide components that can be readily used to build applications that follow one of these patterns. All three patterns mentioned previously try to separate the development of the user interface from the development of the business logic.
The Model View ViewModel (MVVM) pattern is an architectural pattern, which is based on MVC and MVP. Here, the model layer represents the domain-specific data. An example application will include the bank account number, the holder's name, the balance amount, and so on. The model layer represents raw data that needs to be formatted to be consumed by View
. The View
layer consumes the data provided by the model and formats it as required. On the other hand, the behavior of the application or the business logic is encapsulated in a layer called ViewModel
.
There are several frameworks, such as KnockoutJS, KnockbackJS, and Kendo UI, that implement the MVVM pattern. In Kendo UI, the MVVM pattern is at the core of the application framework. The framework provides APIs using which the various components, such as model, ViewModel
, and data binding (that binds the markup to the ViewModel
object) can be accomplished.
How to do it…
To illustrate how the MVVM pattern is implemented in Kendo UI, let's take a look at a very simple example in which the form elements are bound to a ViewModel
object. The ViewModel
object, in this case, would define the model data and also the behavior for one of the elements:
<form id="testView"> Firstname: <input id="firstName" type="text" data-bind="value: fisrtName"> <br/> Lastname: <input id="lastName" type="text" data-bind="value: lastName"> <br/> Fullname: <input id="fullName" type="text" data-bind="value: fullName" readonly> <br/> <input type="submit"> </form>
In the previous code snippet, a form with the id
attribute, testview
, that contains three input elements (textboxes) is created. Note the data-bind
attribute in each one of these input elements. The data-bind
attribute is used to specify the binding between View
elements and the ViewModel
object.
Here, the first input element specifies the value of the data-bind
attribute as value: firstName
. This indicates that the value of the input element is bound to the firstName
model attribute. Now let's define ViewModel
, which encapsulates the model data and behavior:
var viewModel = kendo.observable({ fisrtName: "Sagar", lastName: "Ganatra", fullName: function() { return this.get("fisrtName") + ' ' + this.get("lastName"); } });
A ViewModel
object is created using the Observable
interface on the kendo
object. In this example, the ViewModel
object contains the firstName
, lastName
, and fullName
attributes. Note that while firstName
and lastName
are of the string type, the fullName
attribute is a function that returns a string by concatenating firstName
and lastName
.
Now that we have the markup and ViewModel
, they should be tied to each other. The bind
method is used to bind View
components to ViewModel
:
kendo.bind($('form#testView'),viewModel);
The first argument to the bind function is the View
component referred by using $('form#testView')
and the second argument is the ViewModel
object, viewModel
. Once View
and ViewModel
are bound, any changes made to the model will update View
, and any changes made by the user will be reflected in the model.
How it works...
When you execute the previously mentioned code snippets, the form elements with the values mentioned in the model are populated, as shown in the following screenshot:
Notice that in the markup, the last input element for FullName is read only. It is bound to a fullName
model attribute, which is a function that returns the string concatenated by firstName
and lastName
. When you change the value of either firstName
or lastName
, the fullName
model also gets updated and the same is reflected in the View
class.
In the preceding screenshot, when the value of the Firstname input element is changed, the value of the Fullname attribute also gets updated. Note that the model attributes are updated on a blur event on the View
component.
There's more…
In the previous screenshot, we saw how data binding can be used to set the values of input elements in the form. In addition to setting the value for some of the elements in the page, data binding can also be used to set attribute values (attr
), to set HTML content (html
), hide or show the elements in the page (visible
and invisible
), and so on.
Let's take a look at each one of these scenarios. The attr
binding is used to bind tag
attributes with model attributes in the following code snippet:
<a data-bind="attr: {href: websiteLink}" target="_blank"> Click Here </a>
In the preceding code snippet, the data-bind
attribute has the attr: {href: websiteLink}
value. Here, attr
indicates that the tag attributes are bound to ViewModel
. In this example, the href
attribute of the anchor element is bound to a model attribute, websiteLink
. The ViewModel
object for the same would be as shown in the following code snippet:
<script> var viewModel = kendo.observable({ websiteLink: 'http://www.packtpub.com', }); kendo.bind($('a'),viewModel); </script>
In the code snippet, the ViewModel
object contains only one attribute, websiteLink
. The value for this model attribute is an external link, which is http://www.packtpub.com. When the View
component is bound to the ViewModel
object, the anchor element is updated, which then has an attribute, href
, that refers to the external link mentioned in ViewModel
.
Similarly, the components in View
can be hidden or shown on the page by using the binding attribute, visible
:
<div id="view"> <span id="container" data-bind="visible: isVisible"> Some content here.... </span> <br/> <button id="toggleVisible" data-bind="click: updateVisible"> Toggle Visible </button> </div>
In the previous code snippet, the span element has the data-bind
attribute set to visible: isVisible
. Here, the value of the isVisible
model attribute should be of the Boolean type. If the same is false, then the style attribute of the span element is set to display: none
. Also, the button has the data-bind
attribute set to click: updateVisible
. Here, the button's click
event is bound to the model attribute, updateVisible
. The ViewModel
object for the previous markup is shown in the following code snippet:
var viewModel = kendo.observable({ isVisible: true, updateVisible: function() { this.set('isVisible', !this.get('isVisible')); } }); kendo.bind($('div#view'),viewModel);
The initial value for isVisible
in ViewModel
is set to true and hence the text, Some Content here…
, (inside the span) would be visible. When you click on the toggleVisible button, it toggles the isVisible
model attribute. This will either show or hide the span element.