1

I have seen many a times that a web page has multiple templates for a specific region of the page, one of which is loaded depending on the scenario. Typically a corresponding VM is instantiated at that point and bound to the template.

Does this design pattern not violate OOP principles in that the objects (VMs in this case) should be based on the functional entities that actually exist and not necessarily based on a specific area of the UI.

Lemme substantiate this with an example. Lets say we have a web page dealing with online sales of clothes, shoes and furniture. Once a selection in made among the three in a particular part of the page, a separate template is loaded in another part of the page which is dependent on the selection made. Should my object model be ItemVM<-ShoeVM,ClothesVM,FurntitureVM as per OOP rules, or should it be ParentVM containing ItemSelectorVM (for the UI to select the item type), shoeAdverisementVM (to bind to the shoe template loaded as a result), FurnitureAdvertisementVM and many more VMs for every part of the page?

PKB
  • 81
  • 1
  • 7
  • you can have them as multiple functions inside vm rather creating multiple viewModels (depends). – super cool Aug 03 '15 at 10:19
  • 2
    Keep in mind that your viewmodels *are* intended to model the view (the UI). There should also be underlying models that model the functional entities. – Roy J Aug 03 '15 at 12:43
  • One advantage I see of having multiple view models is that one template in unloaded at runtime, that view model class can be disposed thus avoid scenarios where invalid / stale data is retrieved from the old view model. – PKB Aug 04 '15 at 06:12

1 Answers1

2

When you say OOP principles, I'm assuming you are referring to SOLID.

View Models model the UI and do not have to be coupled to a single template. You may use a view model with a template that has been created to target desktops and then reuse that view model with a template that has been created to target mobiles.

Should my object model be ItemVM<-ShoeVM,ClothesVM,FurntitureVM as per OOP rules, or should it be ParentVM containing ItemSelectorVM (for the UI to select the item type), shoeAdverisementVM (to bind to the shoe template loaded as a result), FurnitureAdvertisementVM and many more VMs for every part of the page?

This depends on the complexity of the application and domain.

Let's assume that your UI is doing more than just iterating over an ObservableArray<T> where T could be of type Shoe or Furniture. And let's also assume that ShoeAdvertisementVM is not some decorator of ItemVM<T>.

Now, lets say you have FurnitureAdvertisementVM and ShoeAdverisementVM that look like this:

function FurnitureAdvertisementVM(furniture){
   this.items = ko.observableArray(furniture);
   this.doSomething = function(){
      // doing something
   }
}

function ShoeAdverisementVM(shoes){
   this.items = ko.observableArray(shoes);
   this.doSomething = function(){
      // doing something
   }
}

var shoeVM = new ShoeAdverisementVM(shoes);
var furnitureVM = new FurnitureAdverisementVM(furniture);

If the two view models are pretty much a copy and paste job and the only difference is the name of the view model and the type of the items that go into the collection then it's probably worth changing the implementation into something generic like this:

function ItemsVM(items){
   this.items = ko.observableArray(items);
   this.doSomething = function(){
      // doing something
   }
}

var shoeVM = new ItemsM(shoes);
var furnitureVM = new ItemsVM(furniture);

But if each view model has some very specific properties and functions, for example:

function FurnitureAdvertisementVM(furniture, someOtherDependency){
   this.items = ko.observableArray(furniture);
   this.doSomething = function(){
      // doing something
   }
   this.propertyIsBoundToATextbox = ko.observable();
   this.clickHandlerUsesDependency = function(ctx){
      if(ctx.SomeCondition){
         someOtherDependency.doSomething();
      }
   }
}

function ShoeAdverisementVM(shoes){
   this.items = ko.observableArray(shoes);
   this.doSomething = function(){
      // doing something
   }
   this.propertyIsBoundToACheckbox = ko.observable();
}

Then it it is not as clear cut as making both view models generic.

Roy J commented:

your viewmodels are intended to model the view (the UI)

Which is so true, so if the views have different requirements, i.e. one has 3 checkboxes the other has loads of buttons etc, then you are likely going to need different view models.

If you want to factor out some of common functionality into separate classes then you can:

function ItemService(items){
   this.items = ko.observableArray(items);
   this.doSomething = function(){
      // doing something
   }
}

function FurnitureAdvertisementVM(itemService, someOtherDependency){
   this.service = itemService;
   this.propertyIsBoundToATextbox = ko.observable();
   this.clickHandlerUsesDependency = function(ctx){
      if(ctx.SomeCondition){
         someOtherDependency.doSomething();
      }
   }
}

function ShoeAdverisementVM(itemService){
   this.service = itemService;
   this.propertyIsBoundToACheckbox = ko.observable();
}
Community
  • 1
  • 1
Anish Patel
  • 4,332
  • 1
  • 30
  • 45
  • Thanks! So youre suggesting that the 2nd approach in the question is what we should follow and have a good object model in place for that. – PKB Aug 05 '15 at 13:30
  • @PKB no problem buddy – Anish Patel Aug 05 '15 at 13:44
  • Agree with you. The VMs should reflect the UI elements. But also that we should reuse VMs wherever possible to avoid bloating up the number. I think now where my thought leads me is to the interface between the VMs (which is tied to the UI design) and the model (which is tied to the actual object hierarchy) in the scenario that they are not aligned. – PKB Aug 05 '15 at 13:44