Spero Larres
Spero Larres

Reputation: 435

extending ko.viewmodel on each array element fails

I can create a View Model such that an array of clickable div's, with favorite/nonfavorite type icons, works correctly. I use knockout to bind the css style, which has the appropriate background image set. The Favorite function is this:

function Favorite() {

  var self = this;
  self.isFavorite = ko.observable();
  self.toggleMe = function(ctx) {
    self = ctx;
    var val = self.isFavorite();
    self.isFavorite(!val);
  };
  self.isFavoriteClass = ko.computed(function() {

    if (self.isFavorite() === true)
      return 'favorite';
    else
      return 'notFavorite';
  });
}

HTML:

<div data-bind="css: {favorite: isFavorite()==true, 
                      notFavorite: !isFavorite() }  ,
                      event: {click: toggleMe}" 
                      style="border:solid red;">

</div>

plnkr: http://plnkr.co/edit/LEwQd544rxrzhNyoyO5F?p=preview

However, I want to achieve the same effect with the ko.viewmodel plugin. (Well, I'd like to also be able to do is with ko.mapping :-) ) ko.viewmodel plugin reference is here:
http://coderenaissance.github.io/knockout.viewmodel/

The syntax for extending view models created with ko.viewmodel is very simple. Here is their example:

options:{ 
    extend:{
        "{root}.users[i]": function(user){
            user.isDeleted = ko.observable(false);
        }
    }
};

viewmodel = ko.viewmodel.fromModel(model,options));

I can successfully bind data to my view model using ko.viewmodel: plnkr: http://plnkr.co/edit/afr9B8lPe1W3jCTrfH3w?p=preview

However, when I try to extend the viewmodel, so that the click-toggling behavior is also present, it fails with the messages:

Uncaught ReferenceError: Unable to process binding "with: function(){return FavoritesVm }" Message: Unable to process binding "foreach: function (){return favorites() }" Message: Unable to process binding "event: function (){return {click:toggleFavorite()} }" Message: toggleFavorite is not defined

Here is my options object, that I tried to use:

var myOptions = { 
    extend:{
        "{root}.favorites[i]": function(favorite){
            favorite.toggleFavorite = ko.computed(function() {
              debugger; 
                // self = this;
                // var val = self.isFavorite();
                // self.isFavorite(!val);
                // return favorite;
                var val = this.isFavorite();
                this.isFavorite(!val);
                return favorite;
              }, this);
        }
    } 
}; 

plnkr: http://plnkr.co/edit/1gez79Q0kvPlRUYFcOfS?p=preview

N.B.: There a similar question to this one, on SO, regarding extending ko.viewmodel to add a ko.computed() on the {root}. However, I want to add a method to each {root}.favorite[i]. See How can I map a field computed using ko.viewmodel and http://jsfiddle.net/sublimejs/L6Wm3/8/

Upvotes: 2

Views: 177

Answers (1)

JotaBe
JotaBe

Reputation: 39055

The problem is that you're mapping directly the favorites array, not the FavoritesVm, so, what you can't specify {root}.favorites[i], because you're in favorites context, and not in the FavoritesVm context. (i.e. that code tries to find a favorites property inside the favorites themselves)

So, to specify that you want to modify each element in favorites, you simply has to sepcify "[i]":

var myOptions = { 
  extend:{"[i]": function(f) {
      f.toggleFavorite = function() {
        f.isFavorite(!f.isFavorite());
      }
    }
  } 
};

NOTE: besides you use too much code for a simple task, you can simply toggle the value as shown in my code, i.e. f.isFavorite(!f.isFavorite());

Click to see the working plunker

Upvotes: 1

Related Questions