JS Knockout ViewModel function call inside foreach binding

Consider a knockout view model:

var data = [{ id: 1, name: "John Doe" }, { id: 2, name: ""}, { id: 3, name: "Peter Parker"}]; var viewModel = { items: ko.observableArray(data) }; viewModel.showName = function (name) { console.log(this); return name && name.length > 0; }; viewModel.removePerson = function () { console.log(this); }; ko.applyBindings(viewModel); 

Using this view:

 <ul data-bind="foreach: items"> <li><span data-bind="text: id"></span> <span data-bind="visible: $root.showName(name)">Yes! show the name</span> <a href="#" data-bind="click: $root.removePerson">Remove</a> </li> </ul> 

You can see it in action here: http://jsfiddle.net/SmW35/8/

In this case, when someone clicks the β€œDelete” link, and KO calls the showName function, the β€œthis” object inside the function is the object with the current element, for example, if I click β€œdelete” in element 2, β€œthis” is { id: 2, name: ""} However, when KO binds "visible" and calls the showName function, the "this" object does not contain the current element, and you need to pass the "name" to the function (or you can use $ data).

So, I have 2 questions:

  • Here you can call the showName function from the view without passing a name or $ data (similar behavior than with the Remove link)
  • If not, then something is wrong? I have an interesting discussion with a workmate who believes that this is not the case because you are sending data from the view ($ root.showName (name)), and then this is not a "clean" MVVM template. He suggests creating a custom KO binding to achieve functionality. In my opinion, this is killing flies with a tank, but I am very curious to find out if there is another way, or you also think that I am not making a clean MVVM template with my code.
+7
javascript mvvm
source share
1 answer

In a sense, your colleague has a point. I personally would not create a custom binding to handle this (throughout the subjective note, custom bindings are more suitable if there is a special way of communication between the view and presentation model, see this post for a great explanation when to use them).

On the side of the note, if we examine the custom binding option, I think you can do something like a textIfNotEmpty binding handler that combines text and visible in one. On the other hand, if the functionality of showName remains as simple as you, you can also:

 <span data-bind="visible: !!name, text: name"></span> 

In any case, I would prefer the following ...

The main problem is IMO, that the view model violates the principle of shared responsibility: the showName functionality should be responsible for representing the model representing the element.

 var Item = function(data) { var self = this; self.id = data.id; self.name = ko.observable(data.name); // or plain "self.name = data.name;" if you don't need 2way binding self.showName = ko.computed(function() { return self.name() && self.name().length > 0; }); } 

Now you can easily link like this:

 <ul data-bind="foreach: items"> <li><span data-bind="text: id"></span> <span data-bind="visible: showName">Yes! show the name</span> <a href="#" data-bind="click: $root.removePerson">Remove</a> </li> </ul> 

Which also allows you to rewrite removePerson to this:

 viewModel.removePerson = function (person) { console.log(person); }; 

This requires you to do a little extra work to build an observable array, but it's worth it because it clearly shares all the problems. This can be done as follows:

 var viewModel = { items: ko.observableArray(data.map(function(item) { return new Item(item); })) }; 

See this script for a demonstration of the above.

+5
source share

All Articles