1

I've been using KnockoutJS for some time now, mainly on small-to-medium sized projects. However, I'm now working on a very large project with many different views of the same data. For example, a product may be displayed to the customer, allowing them to add products to their shopping cart, and also to an administrator, allowing them to edit the product name, price and available stock. I want to be able to use the same model in both cases, but designing a solution with KnockoutJS is proving difficult. Specifically, I am having two issues:

I want to be able to re-use certain "view" functionality without repeating myself. For example, in both the customer- and administrator- view of products, clicking the product thumbnail displays the product image in fullscreen. Instead of each view containing the same verbose binding code (e.g., <img data-bind="event:{click:function codeToZoom(){}}"/> in both views), I can move the binding information to the model itself. So the above becomes <img data-bind="event:imageEvents()"/>. However, doing the above forces me to include code that responds to user input in my model, which violates the single-responsibility principle - e.g., the model is reponsible for business logic, not responding to user input. If I decided I wanted an administrator clicking the thumbnail to open an "Upload New Image" dialog, then I'd need to implement a imageEventsForAdministrator() function.

I've asked people how they deal with the above, and their answer has been "Write two different models." This sounds good until you realise that a product has a lot of embedded logic, and writing two different models forces you to duplicate that logic. So:

According to KnockoutJS, what is the recommended approach for separating business logic from responding to events/user-input?

Magnus
  • 101
  • 5

1 Answers1

2

Your post is interesting and understandable... yet really rather broad for Stack Overflow.

To answer some of your more specific subquestions though:

I want to be able to re-use certain "view" functionality without repeating myself.

There are two typical constructs for tackling this issue:

  • Use templates to do so. Knockout will keep the template as a "prototype" view, and instantiate new instances (and clean up) as needed.
  • Load view html dynamically as "partial views" if you will, and use the appropriate applyBindings overload to render those bits only when needed.

For example, [...] clicking the product thumbnail displays the product image in fullscreen [...] <img data-bind="event:{click:function codeToZoom(){}}"/> in both views

Well, there's several options here. I find that if you want you can actually de-duplicate code as much as theoretically possible. First, you can usually do this:

<img data-bind="event:{click: myHandler}"/>

And you can use some kind of prototypical or classical inheritance on your view models to make sure myHandler is only defined once.

Again, if you find yourself repeating the <img... bit: use KO templates.

Moving on:

This sounds good until you realise that a product has a lot of embedded logic, and writing two different models forces you to duplicate that logic.

In that case you're probably violating the SRP. You probably need to take that logic out of your view models and place it somewhere else (e.g. in controllers, DALs, etc.). But that topic is really broad, our sister site has an entire tag dedicated to it.

Finally, you ask:

According to KnockoutJS, what is the recommended approach for separating business logic from responding to events/user-input?

KnockoutJS recommends no particular approach.

That is, KO is an MVVM library, and as such does nudge you in a certain direction, but it doesn't really force you to take any particular approach with this. It's really up to you to write and structure infrastructure and code for common business logic, think up a good inheritance strategy or something similar, etc.

So it's up to you.

As a footnote, if you haven't already, I'd recommend running through some AngularJS tutorials as well. It has MVVM style bindings, but it is IMO more opinionated about how to solve the problems you're facing. (This is not advice to switch; merely advice to find inspiration in KO-alternatives.)

Community
  • 1
  • 1
Jeroen
  • 60,696
  • 40
  • 206
  • 339