MVVM (Model View ViewModel) is an architectural pattern based on MVC and MVP, which attempts to more clearly separate the development of user interfaces (UI) from that of the business logic and behavior in an application. To this end, many implementations of this pattern make use of declarative data bindings to allow a separation of work on Views from other layers.
This facilitates UI and development work occurring almost simultaneously within the same code base. UI developers write bindings to the ViewModel within their document markup (HTML), where the Model and ViewModel are maintained by developers working on the logic for the application (Figure 10-3).
MVVM (by name) was originally defined by Microsoft for use with Windows Presentation Foundation (WPF) and Silverlight, having been officially announced in 2005 by John Grossman in a blog post about Avalon (the codename for WPF). It also found some popularity in the Adobe Flex community as an alternative to simply using MVC.
Prior to Microsoft adopting the MVVM name, there was however a movement in the community to go from MVP to MVPM: Model View PresentationModel. Martin Fowler wrote an article on PresentationModels back in 2004 for those interested in reading more about it. The idea of a PresentationModel had been around much longer than this article; however, it was considered the big break in the idea and greatly helped popularize it.
There was quite a lot of uproar in the âalt.netâ circles after Microsoft announced MVVM as an alternative to MVPM. Many claimed the companyâs dominance in the GUI world was giving them the opportunity to take over the community as a whole, renaming existing concepts as they pleased for marketing purposes. A progressive crowd recognized that while MVVM and MVPM were effectively the same idea, they came in slightly different packages.
In recent years, MVVM has been implemented in JavaScript in the form of structural frameworks such as KnockoutJS, Kendo MVVM, and Knockback.js, with an overall positive response from the community.
Letâs now review the three components that compose MVVM.
As with other members of the MV* family, the Model in MVVM represents domain-specific data or information that our application will be working with. A typical example of domain-specific data might be a user account (e.g., name, avatar, email) or a music track (e.g., title, year, album).
Models hold information, but typically donât handle behavior. They donât format information or influence how data appears in the browser, as this isnât their responsibility. Instead, formatting of data is handled by the View, whilst behavior is considered business logic that should be encapsulated in another layer that interacts with the Model: the ViewModel.
The only exception to this rule tends to be validation, and itâs considered acceptable for Models to validate data being used to define or update existing models (e.g., does an email address being input meet the requirements of a particular regular expression?).
In KnockoutJS, Models fall under the above definition, but often make Ajax calls to a server-side service to both read and write Model data.
If we were constructing a simple Todo application, a KnockoutJS model representing a single Todo item could look as follows:
var
Todo
=
function
(
content
,
done
)
{
this
.
content
=
ko
.
observable
(
content
);
this
.
done
=
ko
.
observable
(
done
);
this
.
editing
=
ko
.
observable
(
false
);
};
You might notice in the above snippet that we are calling the
method observable()
on the KnockoutJS
namespace ko
. In KnockoutJS,
observables are special JavaScript objects that can notify subscribers
about changes and automatically detect dependencies. This allows us to
synchronize Models and ViewModels when the value of a Model attribute is
modified.
As with MVC, the View is the only part of the application that users actually interact with. They are an interactive UI that represents the state of a ViewModel. In this sense, the view is considered active rather than passive, but this is also true for views in MVC and MVP. In MVC, MVP, and MVVM, a view can also be passive, but what does this mean?
A passive View only outputs a display and does not accept any user input. Such a view may also have no real knowledge of the models in our application and could be manipulated by a presenter. MVVMâs active View contains the data bindings, events, and behaviors, which requires an understanding of the ViewModel. Although these behaviors can be mapped to properties, the View is still responsible for handling events from the ViewModel.
Itâs important to remember the View isnât responsible here for handling state; it keeps this in sync with the ViewModel.
A KnockoutJS View is simply a HTML document with declarative bindings to link it to the ViewModel. KnockoutJS Views display information from the ViewModel, pass commands to it (e.g., a user clicking on an element), and update as the state of the ViewModel changes. Templates generating markup using data from the ViewModel can however also be used for this purpose.
To give a brief initial example, we can look to the JavaScript MVVM framework KnockoutJS for how it allows the definition of a ViewModel and its related bindings in markup.
Here is the code for the ViewModel:
var
aViewModel
=
{
contactName
:
ko
.
observable
(
"John"
);
};
Here is the code for the View:
<
input
id
=
"source"
data
-
bind
=
"value: contactName, valueUpdate: "
keyup
" /></p>
<div data-bind="
visible
:
contactName
().
length
>
10
"
>
You
have
a
really
long
name
!
<
/div>
Our input text box (source) obtains its initial value from
contactName
, automatically updating
this value whenever contactName changes. As the data binding is two-way,
typing into the text box will update contactName
accordingly so the values are
always in sync.
Although implementation specific to KnockoutJS, the <div>
containing the âYou have a really long name!â text also
contains simple validation (once again in the form of data bindings). If the input exceeds 10
characters, it will display; otherwise, it will remain hidden.
We can return to our Todo application for a more advanced example. A trimmed down KnockoutJS View, including all the necessary data bindings, may look as follows:
<
div
id
=
"todoapp"
>
<
header
>
<
h1
>
Todos
<
/h1>
<
input
id
=
"new-todo"
type
=
"text"
data
-
bind
=
"value: current,
valueUpdate: "
afterkeydown
", enterKey: add"
placeholder
=
"What needs to be done?"
/>
<
/header>
<
section
id
=
"main"
data
-
bind
=
"block: todos().length"
>
<
input
id
=
"toggle-all"
type
=
"checkbox"
data
-
bind
=
"checked: allCompleted"
>
<
label
for
=
"toggle-all"
>
Mark
all
as
complete
<
/label>
<
ul
id
=
"todo-list"
data
-
bind
=
"foreach: todos"
>
<!--
item
-->
<
li
data
-
bind
=
"css: { done: done, editing: editing }"
>
<
div
class
=
"view"
data
-
bind
=
"event: { dblclick: $root.editItem }"
>
<
input
class
=
"toggle"
type
=
"checkbox"
data
-
bind
=
"checked: done"
>
<
label
data
-
bind
=
"text: content"
><
/label>
<
a
class
=
"destroy"
href
=
"#"
data
-
bind
=
"click: $root.remove"
><
/a>
<
/div>
<
input
class
=
"edit' type="
text
"
data-bind="
value
:
content
,
valueUpdate
:
"afterkeydown"
,
enterKey
:
$root
.
stopEditing
,
selectAndFocus
:
editing
,
event
:
{
blur
:
$root
.
stopEditing
}
"
/>
<
/li>
<
/ul>
<
/section>
<
/div>
Note that the basic layout of the markup is relatively
straightforward, containing an input text box (new-todo
) for adding new items, togglers for
marking items as complete, and a list (todo-list
) with a template for a Todo item in
the form of an li
.
The data bindings in the above markup can be broken down as follows:
The input text box
new-todo
has a data binding for thecurrent
property, which is where the value of the current item being added is stored. Our ViewModel (shown shortly) observes thecurrent
property and also has a binding against theadd
event. When the Enter key is pressed, theadd
event is triggered, and our ViewModel can then trim the value ofcurrent
and add it to the Todo list as needed.The input checkbox
toggle-all
can mark all of the current items as completed if clicked. If checked, it triggers theallCompleted
event, which can be seen in our ViewModel.The item
li
has the classdone
. When a task is marked as done, the CSS classediting
is marked accordingly. If double-clicking on the item, the$root.editItem
callback will be executed.The checkbox with the class
toggle
shows the state of thedone
property.A label contains the text value of the Todo item (
content
).There is also a remove button that will call the
$root.remove
callback when clicked.An input text box used for editing mode also holds the value of the Todo item
content
. TheenterKey
event will set theediting
property to true or false.
The ViewModel can be considered a specialized Controller that acts as a data converter. It changes Model information into View information, passing commands from the View to the Model.
For example, let us imagine that we have a model containing a
date
attribute in UNIX format (e.g., 1333832407).
Rather than our models being aware of a userâs view of the date (e.g.,
04/07/2012 @ 5:00pm), where it would be necessary to convert the address
to its display format, our model simply holds the raw format of the
data. Our View contains the formatted date, and our ViewModel acts as a
middleman between the two.
In this sense, the ViewModel might be looked upon as more of a Model than a View, but it does handle most of the Viewâs display logic. The ViewModel may also expose methods for helping to maintain the Viewâs state, update the model based on the actions on a View, and trigger events on the View.
In summary, the ViewModel sits behind our UI layer. It exposes data needed by a View (from a Model) and can be viewed as the source our Views go to for both data and actions.
KnockoutJS interprets the ViewModel as the representation of data and operations that can be performed on a UI. This isnât the UI itself nor the data model that persists, but rather a layer that can also hold the yet to be saved data a user is working with. Knockoutâs ViewModels are implemented JavaScript objects with no knowledge of HTML markup. This abstract approach to their implementation allows them to stay simple, meaning more complex behavior can be more easily managed on top as needed.
A partial KnockoutJS ViewModel for our Todo application could thus look as follows:
// our main ViewModel
var
ViewModel
=
function
(
todos
)
{
var
self
=
this
;
// map array of passed in todos to an observableArray of Todo objects
self
.
todos
=
ko
.
observableArray
(
ko
.
utils
.
arrayMap
(
todos
,
function
(
todo
)
{
return
new
Todo
(
todo
.
content
,
todo
.
done
);
}));
// store the new todo value being entered
self
.
current
=
ko
.
observable
();
// add a new todo, when enter key is pressed
self
.
add
=
function
(
data
,
event
)
{
var
newTodo
,
current
=
self
.
current
().
trim
();
if
(
current
)
{
newTodo
=
new
Todo
(
current
);
self
.
todos
.
push
(
newTodo
);
self
.
current
(
""
);
}
};
// remove a single todo
self
.
remove
=
function
(
todo
)
{
self
.
todos
.
remove
(
todo
);
};
// remove all completed todos
self
.
removeCompleted
=
function
()
{
self
.
todos
.
remove
(
function
(
todo
)
{
return
todo
.
done
();
});
};
// writeable computed observable to handle marking all complete/incomplete
self
.
allCompleted
=
ko
.
computed
({
// always return true/false based on the done flag of all todos
read
:
function
()
{
return
!
self
.
remainingCount
();
},
// set all todos to the written value (true/false)
write
:
function
(
newValue
)
{
ko
.
utils
.
arrayForEach
(
self
.
todos
(),
function
(
todo
)
{
// set even if value is the same, as
subscribers
are
not
notified
in
that
case
todo
.
done
(
newValue
);
});
}
});
// edit an item
self
.
editItem
=
function
(
item
)
{
item
.
editing
(
true
);
};
..
Here, we are basically providing the methods needed to add, edit,
or remove items as well as the logic to mark all remaining items as
having been completed. Note that the only real difference from previous
examples in our ViewModel are observable arrays. In KnockoutJS, if we wish to detect and respond to
changes on a single object, we would use observables
. If, however, we wish to detect
and respond to changes of a collection of things, we can use an observableArray
instead. A simpler example of
how to use observable arrays may look as follows:
// Define an initially an empty array
var
myObservableArray
=
ko
.
observableArray
();
// Add a value to the array and notify our observers
myObservableArray
.
push
(
'A new todo item'
);
The complete Knockout.js Todo application we reviewed above can be grabbed from TodoMVC.
Views and ViewModels communicate using data bindings and events. As we saw in our initial ViewModel example, the ViewModel doesnât just expose Model attributes but also access to other methods and features such as validation.
Our Views handle their own user interface events, mapping them to the ViewModel as necessary. Models and attributes on the ViewModel are synchronized and updated via two-way data binding.
Triggers (data triggers) also allow us to further react to changes in the state of our Model attributes.
While it may appear that the ViewModel is completely responsible for the Model in MVVM, there are some subtleties with this relationship worth noting. The ViewModel can expose a Model or Model attributes for the purposes of data binding and can also contain interfaces for fetching and manipulating properties exposed in the view.
Get Learning JavaScript Design Patterns now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.