Kendo UI provides a simple and powerful MVVM framework through their observable object and binding in the DOM. To create a basic viewmodel the syntax is very simple, and this is how their documentation educates you to create your viewmodels.
var viewModel1 = new kendo.data.ObservableObject({ field1: "value1", field2: "value2" });
If you have an object-oriented background or want to use object-oriented design in JavaScript to create your viewmodels there is not much guidance on doing this. If you understand JavaScript's prototype inheritance you can achieve this, but this requires advanced knowledge of JavaScript and is difficult to implement correctly. Instead, I'm going to guide you through using Kendo's Class object to handle that for us, and to provide an easy to extend viewmodel base that derived viewmodels can extend with a lightweight syntax. This will also provide a proper viewmodel constructor for initialization, in which we can pass in dependencies.
To do this, we are going to actually derive from ObservableObject
because it extends Class
already, but provides us with the observable pattern we need for data-binding, along with several important methods.
- get - gets the value of an observable property on the viewmodel
- set - sets the value of an observable property on the viewmodel
- trigger - raises an event on the viewmodel
- bind - subscribes to an event on the viewmodel
var ViewModel = kendo.data.ObservableObject.extend({ init: function() { kendo.data.ObservableObject.fn.init.call(this); } });
This is all you need, so save this into a ViewModel.js
file. The init
function is your constructor and will be called automatically when you create an instance of the object using the new
keyword, and the single line of code in there calls the base init method of ObservableObject
and initializes the observable behaviors. Without this you will not truly have an observable object, and your bindings will not update. Keep in mind that kendo.data
is basically like a namespace, and fn
is just an alias for prototype
. Always remember that Kendo is built on top of jQuery, so you will find many constructs from jQuery throughout.
Your first viewmodel
Now we can extend ViewModel
and add constructor parameters, properties and methods.
var CustomerViewModel = ViewModel.extend({ init: function(name) { ViewModel.fn.init.call(this); this.name = name; }, name: null, changeName: function(newName) { if (newName !== this.name) { this.set("name", newName); this.trigger("namechange"); } } });
Inside init
we initialize the observable object base and set the name property to the value of the name constructor parameter. It is safe to use the assignment operator =
here because the field has not yet been initialized as an observable object, but you should note that it is only safe to do this inside init. In other functions of your viewmodel, inside and out, you will need to use the set
function of the viewmodel to set a value on a property. This is because once initialized, fields of the viewmodel are all initialized as observable object's as well, which enables the binding capabilities through the observable pattern. The assignment operator would replace the entire observable object with the actual value and bindings would break. You can see this distinction inside the changeName
function.
I included a changeName
function for the sake of showing that there isn't anything that different about declaring functions on your viewmodel. As a matter of fact, we could have attached these to the viewmodel inside the constructor instead of defining them as fields, so an alternative syntax could seen below.
var CustomerViewModel = ViewModel.extend({ init: function(name) { ViewModel.fn.init.call(this); this.name = name; this.changeName = function(newName) { ... } } });
This would be more typical of JavaScript code, but coming from a OOP and C# background I prefer to see properties and functions "declared" on the object rather than attached inside the constructor. As the viewmodel grows in size you will not want your init function to be cluttered either.
Using your viewmodel
So, that's it. A very easy way to take kendo's observable and use prototype inheritance to create a viewmodel base, which results in a much better way to create and maintain viewmodels. This allows you to do all sorts of things and reuse code. You can derive viewmodels from viewmodels and create many different types from a common base type, and you have a consistent way to create them. To use your viewmodel, there isn't anything different about that either; you still create your viewmodel and bind to it.
var viewModel = new CustomerViewModel("John Doe"); kendo.bind(document.body, viewModel);
Raising and subscribing to events
I mentioned that by extending ObservableObject
that we get a trigger
function. This is nice because this is how you will want to raise events from your viewmodel to notify the view of something that has occurred, or will occur. The changeName
function demonstrates this by raising an event named namechange
, but now you need to subscribe to this event so you can do something with it.
var viewModel = new CustomerViewModel("John Doe"); kendo.bind(document.body, viewModel); viewModel.bind("namechange", function() { alert("name changed!"); });
Just as you would bind events using jQuery, extending ObservableObject
also provides a bind
method with almost identical syntax, and similar to subscribing to events with on
. When an event with the specified name is raised, the callback function will be invoked and you can respond to that event. This seems simple and minute, but this is one of the most important features of your viewmodel that will help you keep it decoupled from your view.
Final Word
I hope this article helps you write better JavaScript and create better viewmodels working with kendo-ui. While a small thing, these possibilities make a major difference in large codebase's. You can use the same approach to create a View
class as well, which I might do an article on if time permits. Remember your design principles, and use the constructor to pass in dependencies (such as kendo, jQuery, or other JavaScript services) and you will find you can write testable, clean, and decoupled viewmodels just as you would using MVVM with something like WPF, or other MVVM frameworks. Lastly, remember you can derive directly from kendo.Class
for the same capabilities for general purpose JavaScript classes that don't need observable behaviors; maybe you just need constructors and a simple inheritance model.