Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM with Knockout.js

I'm trying to implement a MVVM based Single Page Application and currently am using the framework Knockout.js to handle the viewmodel/view portion of MVVM. I'm confused though, as every example I've looked at for implementing Knockout involves saving an entire viewmodel to the database. Aren't these examples missing a "model" step, where the viewmodel syncs with the data-layer model and the model does validation / Server synchronization.

I would like to have multiple different templates/views on the single page each with a different viewmodel. Another thing that I find is missing with knockout.js is synchronizing a single model (not viewmodel) across different views. I don't think it makes sense to have one giant viewmodel that every view shares, so I was thinking that each view would have its own viewmodel but each viewmodel would sync with the fields of just a few application-wide models that are needed for each view.

The page I'm working on fetches a giant model (30+ fields, multiple layers of parent/child relationships) and I think it makes sense to just have all of my viewmodels sync with this model. I have investigated Knockback.js (which combines knockout.js and backbone.js) however I ended up re-writing the majority of the functions such as fetch, set, save, because the page is getting data from an API (and I can't just sync an entire model back and forth with the server) so I decided against it.

visual example of my application:

(model layer) M | M

(viewmodel/view layer) VM-V | VM-V | VM-V | VM-V


another example

An example model would be User = {firstName: "first", lastName: "last", ... }

one viewmodel only needs first name, another viewmodel only needs last name
ViewModelA={firstName: app.User.firstName()}
ViewModelB={firstName: app.User.lastName()}

Is the only way to do this to define a pub/sub system for Model and Viewmodel changes? Is this even good/maintainable architecture? Am I missing a basic concept here? All advice welcome.

like image 844
Phil Baylog Avatar asked Jun 28 '12 17:06

Phil Baylog


3 Answers

If I read this correctly, there's a lot of questions in here all focused on how to build a MVVM / SPA with Knockout. There are a few things to tackle, as you pointed out. One is how to communicate between viewmodel/view pairs.

Master ViewModel One way to do that is to have a master viewmodel as the answer from @Tyrsius. Your shell could have a viewmodel that binds more available data. The master viewmodel could orchestrate the child view models, too. If you go this route then you have to be careful to bind the outer shell to the master viewmodel and the inner ones to specific HTML elements in the DOM. The master viewmodel could facilitate the communication between them if need be.

Decoupled View/ViewModel Pairs Another option is to use viewmodel/view pairs and no master viewmodel. Each view is loaded into a region of the DOM and bound on its own. They act as separate units and are decoupled from one another. You could use pub/sub to then talk between the, but if all you need is a way for the data to be synched through observables, Knockout provides many options. The one I like is to have each viewmodel surface model objects. So a view has a viewmodel which surfaces data (from a model) that is specific for the view. So many viewmodels may surface the same model in different ways. So when a view updates a viewmodel property (that is in a model) it then ripples to any other loaded viewmodel that also uses the same model.

DataContext Going a bit further, you could create a datacontext module that manages the data in the models. You ask the datacontext for a model (ex: list of customers) and the datacontext checks if it has them cahced already and if not, it goes and gets them from an ajax call. Either way that is abstracted from the view model and models. The datacontext gets the data and returns a model(s) to the viewmodel. This way you are very decoupled, yet you can share the data (your models) via the datacontext.

I could go on and on ... but please tell me if this is answering your question. If not, happy to answer any other specifics.

** Disclaimer: I'm building a Pluralsight course on SPA's (using Knockout and this strategy) :-)

like image 77
John Papa Avatar answered Nov 18 '22 18:11

John Papa


This is a popular field of interest right now, so I expect you will get some better answers, but here goes.

The Model

Yes, you should absolutely have a server-side representation of the data, which is your model. What this is depends on your server, and your database. For MVC3, this is your entity model. For Django or Ruby, your will have defined db models as part of your db setup. This part is up to your specific technology. But agian, yes you should have a model, and the server should absolutely perform data-validation.

The Application (ViewModel)

It is recommended that your views each have their own viewmodel. Your page could then also have a viewmodel, an App Viewmodel if you will, that keeps track of all of them. If you go this route, the app viewmodel should be responsible for switching between the views, and implementing any other application level logic (like hash bashed navigation, another popular single page tool). This hierarchy is very important, but not always simple. It will come down to the specific requirements of your application. You are not limited to one, flat viewmodel. This is not the only possible method.

Ad Hoc Example:

​var ThingViewModel = function(name, data){
    this.name = ko.observable(name);
    //Additional viewmodel stuffs
};

var AppViewModel = function(initialData){
    //Process initial data    
    this.thing = new ThingViewModel(someName, someData); 

};

I am working on a similar project right now, purely for study (not a real world app), that is hosted here on GitHub, if you would like to take a look at some real exmaples. Note, the dev branch is quite a bit ahead of the master branch at the moment. I'm sure it contains some bad patterns (feel free to point them out, I am learning too), but you might be able to learn a few things from it anyway.

like image 2
Kyeotic Avatar answered Nov 18 '22 16:11

Kyeotic


I have a similarly complex solution in which I am reworking a WPF application into a web version. The WPF version dealt with complex domain objects which it bound to views by way of presenter models.

In the web version I have implemented simplified and somewhat flattened server-side view models which are translated back and forth from / to domain objects using Automapper. Then those server-side view models are sent back and forth to the client as JSON and mapped into / onto corresponding Knockout view models (instantiable functions which each take responsibility for creating their children with sub-mapping options) using the mapping plugin.

When I need to save / validate my UI, I map all or part of my Knockout view model back to a plain Javascript object, post it as JSON, the MVC framework binds it back to server-side view models, these get Automapped back to domain objects, validated and possibly updated by our domain layer, and then a revised full or partial graph is returned and remapped.

At present I have only one main page where the Knockout action takes place but I anticipate that like you, I will end up with multiple contexts which need to deal with the same model (my domain objects) pulled as differing view models depending on what I'm doing with them.

I have structured the server side view model directories etc in anticipation of this, as well as the structure of my Knockout view models. So far this approach is working nicely. Hope this helps.

like image 1
Tom W Hall Avatar answered Nov 18 '22 18:11

Tom W Hall