Build Single Page Apps – Part 7 – MVVM and KnockoutJS | John Papa

John Papa

Evangelist on the loose

Build Single Page Apps – Part 7 – MVVM and KnockoutJS

...

You knew it was coming, and you were right. MVVM and Knockout are 2 passions of mine and of course they are a big part of my upcoming Pluralsight course titled “Building Single Page Apps (SPA) with HTML5, ASP.NET Web API, Knockout and jQuery”. It’s less than 2 weeks to go before it’s due to go live!

You can catch up on the previous posts in this series here:

More on the Code Camper SPA

Part 1 – The Story Begins (What is the Code Camper SPA?)

Part 2 – Client Technologies

Part 3 – Server Technologies (the Data Layer)

Part 4 – Serving JSON with ASP.NET Web API

Part 5 – HTML 5 and ASP.NET Web Optimization

Part 6 – JavaScript Modules

Part 7 – MVVM and KnockoutJS

Part 8 – Data Services on the Client

Part 9 – Navigation, Transitions, Storage and Messaging

Part 10 – Saving, Change Tracking, and Commanding

Part 11 – Responsive Design and Mobility

 

MVVM

I’ve talked and written a lot about Model View ViewModel (MVVM) in the past, and if you aren’t familiar with MVVM you can catch up with a MVVM primer from this article. Are you back yet? Great. The good news for anyone who has done MVVM, MVP or MVC patterns (also known as the MV* patterns) is that you can follow this same pattern with JavaScript and SPA’s. In fact, its growing in popularity. KnockoutJS is a great library that handles data binding and integrates well with MVVM pattern. Together, they make a great pair. Let me explain a bit.

The HTML is our View and the data, which is very likely JSON, are our Model(s). So that covers the MV* part of MVVM. The way these fit together is important. We are apt to reuse Models in multiple Views, so it makes no sense to write redundant code with the copy/paste pattern when we need Person Model information in 4 or 5 Views. And how often is the data that comes from the web services and AJAX calls exactly what a View needs? Most of the time we need to tweak that data, adding new fields here, calculating other ones there, or cherry picking data form multiple Models for a View. This is where it is convenient to create an object that manages the View’s data … the View’s Models … the ViewModel.

The ViewModel can expose model data for the View to consume (via bindings and KnockoutJs). it’s not something that is specific for XAML … it works anywhere we have this separation of concerns.

The screenshot below shows the various types of data that the Code Camper SPA’s Favorites View needs. The data comes from various sources of models including Sessions (standard data), a list of dates of the events (lookup data), and filters. The first 2 come from web services while the latter (the filters) are completely driven by the View’s needs. All of these are aggregated together in a ViewModel.

image

KnockoutJS

We can certainly write jQuery to push the data into the HTML elements, and then pull them back out. That’s a lot of code that we don’t need to write though.  Knockout gives us data binding for that. KnockoutJS is a robust JavaScript data binding library that provides “observable” members which support two way bindings. I don’t cover Knockout in depth in the SPA course, but if you want in depth coverage of KnockoutJS you can catch my Pluralsight course on MVVM and Knockout here.

image

ViewModel Example

OK, I’ll give in … here is a quick example of a simple ViewModel. I shortened the contents of the functions because they are irrelevant really. The key here is that the list of publicly exposed members is at the bottom.

define('vm.speaker',
['ko', 'datacontext', 'config', 'router', 'messenger'],
function (ko, datacontext, config, router, messenger) {
var
// Properties
currentSpeakerId = ko.observable(),
logger = config.logger,
speaker = ko.observable(),
speakerSessions = ko.observableArray(),
validationErrors = ko.observableArray(),
// Knockout Computeds
canEdit = ko.computed(function () {   .......   }),
isDirty = ko.computed(function () {   .......  }),
isValid = ko.computed(function () {   .......  }),
// Methods
activate = function (routeData, callback) {   .......  },
cancelCmd = ko.asyncCommand({
execute: function(complete) {   .......  },
canExecute: function(isExecuting) {   .......  }
}),
canLeave = function () {   .......  },
getSpeaker = function (completeCallback, forceRefresh) {   .......  },
goBackCmd = ko.asyncCommand({
execute: function(complete) {   .......  },
canExecute: function(isExecuting) {   .......  }
}),
saveCmd = ko.asyncCommand({
execute: function(complete) {   .......  },
canExecute: function(isExecuting) {   .......  }
}),
tmplName = function () {   .......  };
return {
activate: activate,
cancelCmd: cancelCmd,
canEdit: canEdit,
canLeave: canLeave,
goBackCmd: goBackCmd,
isDirty: isDirty,
isValid: isValid,
saveCmd: saveCmd,
speaker: speaker,
speakerSessions: speakerSessions,
tmplName: tmplName
};
});

The ViewModel exposes the public members at the bottom, in the return object, and hides the rest. I’ll leave it here for now … there’s a lot of nice goodies in that code that may peak your interest. Next time, I’ll talk a bit more about how data flows throughout the client.

tags: javascript knockout mvvm pluralsight SPA
  • Andrew

    Hi John,
    I’m looking forward for your course, it looks awesome.
    What is the ‘define’ function?
    Is it from the new course or from the existing Knockout course?
    Thanks!

  • http://rainerat.spirit.de/ RainerAtSpirit

    Great to see the revealing module pattern in action. One minor nuance; if you intend to use r.js later down the road to optimize your code it might be beneficial not to define named modules (http://www.requirejs.org/docs/api.html#modulename). Here’s an example that demonstrate that pattern: https://gist.github.com/3237820

  • john

    Andrew – Thanks. The ‘define’ comes from RequireJS (see Part 6 of my series).

  • john

    RainerAtSpirit – Thanks for the comment. Yes, there are some nuances with r.js and RequireJS. I am not using r.js and instead I used ASP.NET Web Opt.

  • Andrew

    John,
    Are you going to use things like DependencyInjection or IoC?
    What about something similar to ViewModelLocator?
    I’m coming from WPF\SL dev world. and I haven’t been able to find guidelines on MVVM using the techniques I used to work with in SL\WPF. Knockout.js is great but I need some good guidelines on how to create views and viewmodels, maybe from a SL\WPF perspective.
    Thanks!

  • http://www.marisic.net Chris Marisic

    I really don’t understand the purpose of absolute SPA, it seems silly to me.
    Using your example above: http://johnpapa.net/Media/Default/Windows-Live-Writer/7323e2fa09cf_145CD/image_5.png
    Why would you not want clicking on sessions or speakers to be a page transition? Sessions and speakers have absolutely nothing to do with favorites. Why do you want to the cost of managing all of those dependencies concurrently?
    I develop apps that every resource is a SPA and that every change to a different resource is a full page transition. It just makes sense. Users don’t understand whether something is ajax or http, they only understand does clicking this do what i expect and do what i expect “fast”.

  • Steven

    ( ko, datacontext, config, router, messenger ) look like helper objects. Is this a part of the glue provided by knockout?
    Looking forward to seeing what comes next. Slightly strange seeing that many ‘statements’ separated by commas… but not too hard on the eyes either.

  • john

    Andrew,
    RequireJS is helping define the dependencies (see the define statements) for each module. We define them and RequireJS will locate them and inject them into the module. So you can think of one of RequireJS’s roles as DI (tho it does much more).
    Relating MVVM in JavaScript to MVVM in XAML … it sound slike you are asking for how to link them together. Views are HTML so we can load them as new pages (reloading them), as templates for a page, or even as a partial page. Liken this to “regions” or “user controls” in XAML. In CodeCamper’s SPA I define a View and load it when navigation is performed. Each navigation “route” has an associated View and optionally a callback . The View is the HTML we load and the callback (if exists) is the ViewModel’s activation method (the things you want to happen when it first kicks off). I follow this pattern in CodeCamper, but there are other ways, too.
    Bottom line is that just like there are many ways to do MVVM loading of Views and VM’s in XAML, we also have options in JS.
    I’m happy to discuss any ideas you have. Great things come from those types of conversations.

  • john

    Chris – It’s a fair point. Not all apps should be SPA’s. The reason this is a SPA is it fit the user story for this app (explained in the first post – Part 1). The data is related to each other: favorites are sessions, and speaker data is related to those as the user always wants something about the speaker when looking at a session. Granted this is just THIS app’s user story. On top of the related data, the user story also defines a need for the Views to be available in slow or no WiFi environments (at a conference). You are running between sessions and want to quickly see what room your next favorite is in. WIth the SPA, you can do that without any network calls or page refreshes. Ifyou want more data, then you have to refresh or hit the server via AJAX.
    I agree with your assessment of developing many SPA’s together in one app. It is very viable to think of an app as a set of SPA’s that have distinct purposes. And I also aree the user doesnt care how its done … just that it is responsive.

  • john

    Steven,
    Thanks for the comments (eveyone).
    These objects you refer to are dependencies for the view model module I show above.
    ko is Knockout
    datacontext is my client side repository
    config is configuration module, that stores all those settings a project always has
    router is the module that handles navigation
    messenger is what handles the pub/sub of messages
    Regarding the “commas”, I think you mean how I use 1 var and then list all variable definitions. I prefer this style so I can have all my definitions up top (google the term hoisting). But it’s absolutely valid to use separate var statements too … I would just be careful there to define them all up top.

  • Tim Johnson

    Will you be covering things like knockback js and when to use one over the other? may be in conjuction for a specific test case that you can come up with?

  • john

    Tim – I am covering a huge laundry list of other tech and libraries but Knockback is not one of them :) Sorry

  • http://www.csel.co.nz Brendan

    John. I love the nice clean looking viewmodel. Do you ever find yourself needing to add a few private “helper” functions to your viewmodel? If so, were do you normally add these? The single “var” approach looks nice but I guess it means any helper functions need to be earlier in the declaration order than when they’re use? An example might be a “close” helper function that is called by “cancel” and at the end of “save”.

  • john

    Brendan ,
    Good questions. Yes, I often need members to be privately accessible within the module as well as ones that should be publicly accessible. The ViewModel has examples of both of private and public members. The only members that are accessible outside of this module are (aka public) are the ones being returned at the end of the module. This pattern allows you to declare as much as you like internally, then only reveal the members of the module via a return object. It’s known as the Revealing Module Pattern.
    Some private members are the validationErrors property and the getSpeakers method.
    I hope this helps!

  • http://www.csel.co.nz Brendan

    Thanks John.
    The intent of my question was more about having to order helper functions first in the “var” order to make sure they were available to use further down the order.
    I think my main issue was that I was calling a private (refresh) function from inside a knockout computed. Because computeds are executed immediately when they are declared (to determine their dependances) my code was complaining that my computed was declared before the refresh function.
    Am I doing something dumb or do I just need to define computeds (that are dependant on other functions) last?
    I appreciate this is a bit of an aside to your blog post but I do appreciate your insight and experience. FYI, I’m considering refactoring about 20 viewmodels to use the pattern you demonstrate in this post. The project is already in production but were constantly adding new functions to it and it would be great if all of our viewmodels looked as tidy as yours! :)

  • john

    Brendan,
    Thanks for clarifying. Yes, you are correct about how computeds work. I find that I list my properties first, then computeds, then methods. This way, when the computeds execute they already know about the properties. Then the methods follow the computeds and can references them.
    Separate var statements are fine too, I just prefer one var so it forces me to avoid any hoisting issues.
    Good luck with the refactoring!

  • Andrew

    Hi John,
    I’ve been thinking for a while about the idea of a markup based UI language(XAML like) in HTML using the UI composition patterns from Silverlight or WPF.
    I am referring to things like for example, being able to have a ContentControl which can contain about anything, or the ItemsControl which render content by ItemsPanel and ItemTemplate.
    There’s a project called Buckshot
    https://github.com/prujohn/Buckshot
    which is trying to do the XAML-like stuff.
    The problem is it’s based on Google’s Dart language.
    I wonder what do you think about the idea of creating a pure javascript little framework which is able to parse a XAML-like XML file and creates controls.
    There are different kind of problems.
    It’s hard I think to achieve the XAML flexibility with the HTML controls.
    For example, I am not sure if having a input type=’button’ as a Button(ContentControl derived) for example being able to contain anything is possible.
    Such a UI framework combined with data-binding using Knockout.js and it will be very handy I think.
    I would love to hear your thoughts on this idea,
    Andrew

  • john

    Andrew,
    Thanks for the thought out comments. It is an intriguing idea and I think there is always room for a new idea and library.
    I recommend that you first create a goal statement for the library. Why would someone want to use your library? What are the benefits? Who is your target audience? Will you maintain it after it goes live? I’m not asking, just saying you should answer those for yourself before you begin. It will help you streamline what you build and stay focused. I did this same process before open sourcing toastr and KoLite.
    My first impression is that it would be cool to have a XAML parsing library, but that 3 concerns jump straight out for me. First, it seems like an extra wrapper and parsing XAML could be slow. Second, XAML formats evolve (Silverlight, WPF, WinRT) so it would have to be a clear focus and evolve. ANyone who knows JavaScript would probably skip this, and those who know XAML might be attracted to it. But they will have to hit JavaScript sooner or later, so is this really helping? I am sure you can answer these and maybe you have something great in mind. They are certainly not intended to turn you away. If you are passionate about it, do it!

  • Andrew

    Thank you John.
    My goal is not having a full XAML parser and port all the features from XAML, but to at least support the basics of UI composition from XAML.
    My idea is the way controls are composed in SL/WPF/XAML is something which would be very useful in Javascript.
    I just found rappidjs, http://www.rappidjs.com
    The way UI can be composed is cool and it’s close to what I was talking about.

    • http://rappidjs.com Marcus K.

      Hey Andrew,

      its nice to hear, that you like rappidjs.
      We also have the opinion that XAML is a very good way to define your UI and configure your application. Through the declarative approach and the use of namespaces, the integration/reuse and wiring of components is very easy.
      You said “it’s close” to what you talked about. What is missing in your opinion? It would help to make the framework even better. Thanks

  • http://DNAfor.NET Chau Nguyen

    Oh, I also asked this on another blog post, but might be more appropriate here.. I’ll keep it short.. just wondering if you or anyone here has any advice on which UI frameworks work well with Knockout. We looked a bit at Kendo and Wijmo.. Kendo and KO is a bit difficult.. there is some project Kendo Knockout project to make them more compatible, but not sure there is much activity on there.. Thanks

  • john

    Hi Chau,
    Thanks for the comments. The project you mentioned is written by Ryan Niemeyer, one of the contributors to Knockout. I definitely recommend looking at his Knockout/Kendo library for using the 2 together. http://rniemeyer.github.com/knockout-kendo/

  • Gomti

    I have gone through the pluralsight course on spa. Its wonderful. I learnt a lot from it.

  • StrandedPirate

    I’m wondering how you handle loading multiple views? I’m building an SPA on top of MVC 3 now and it is going to have a lot of views and knockout templates. I don’t want to have a monolithic page that has every view ever needed by the app all on one page for maintenance and sanity reasons. My first thought was to retrieve each view via ajax calls as needed but am wondering if you already covered your thoughts on this subject on one of your posts?

    • John

      Hi StrandedPirate,

      You could use require.js and its text plugin to get the views as needed, or any other similar technique (like a custom jquery/ajax mmodule).

  • TJay

    Hi John,

    I enjoy reading your posts and watching your tutorial videos on Pluralsight. I am actually doing a commercial project based on everything you are presenting so it is very practical and exciting.

    On a completely irrelevant note, I have noticed in some of your blog posts: using “its” instead of “it’s” and vice versa. It definitely won’t stop me (and probably anyone else for that matter) from reading your posts, but still.

    Keep up the great work and looking forward to future posts.

%d bloggers like this: