4
votes

Knockout.js boasts two-way bindings, and the live examples show how the value binding (when applied to a text input) updates the viewmodel.

However, I've tried to update other bindings (like text) and found that the view does not update the viewmodel. See this example: http://jsfiddle.net/w7t89tuu/

It's unclear from knockout's documentation whether some of their bindings are two-way and others are not. Under what conditions can the view update the viewmodel?

2
They are two-way... you're just not updating the value as you should. If you're going to do it programmatically, you change it via the bound observable. Otherwise you put the value in the input. You went out and did something completely different... - Jeff Mercado
You could try .trigger('change') if you're using jquery. I've used this in unit tests to test 'two-way' bindings on input elements. - CrimsonChris
@JeffMercado, are you saying that IF you want to update the view, and have it update the value, then you can only do that for inputs? - bryanbraun

2 Answers

5
votes

Short answer

Two-way bindings: in general all the bindings related to something that the user can change directly, without the help of special scripts, like form control values or states: value, hasFocus, textInput, checked, selectedOptions. Or any custom binding related to something that the user can change (the typical example of 5 clickable stars implemented as a ko custom binding).

One-way bindings: all the states which cannot be directly changed by the user, for example visible. The user cannot change directly the visibility: it must be done via an script. In this case the script should not change the visibility of the DOM element itself, but should change the bound observable. The first wouldn't update the observable, but the latter would update the visibility of the bound element.

Long answer

If you look how to implement custom bindings you'll understand how they work: Creating custom bindings:

ko.bindingHandlers.yourBindingName = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    // This will be called when the binding is first applied to an element
    // Set up any initial state, event handlers, etc. here
  },
  update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    // This will be called once when the binding is first applied to an element,
    // and again whenever any observables/computeds that are accessed change
    // Update the DOM element based on the supplied values here.
  }
};

If you look further you'll see what init callback can be used for:

Knockout will call your init function once for each DOM element that you use the binding on. There are two main uses for init:

1.To set any initial state for the DOM element

  1. To register any event handlers so that, for example, when the user clicks on or modifies the DOM element, you can change the state of the associated observable

The key is in the second point: if the binding handles some kind of event, it will modify the observable value, which is what you consider the "way back" of the "two-way binding".

So, any binding that handles events to update the observable work as "two-way" binding.

3
votes

Your problem is that you're not updating the observable value, you're directly modifying the element's text. The same thing happens if you directly update the input element's value - you're not modifying the observable value at all. If you want to update the observable value, you can instead use:

this.changeName = function() {
    this.name('Bob');
};

JSFiddle demo.