How to use a one way binding in knockout
Asked Answered
F

3

8

I have a viewModel like this:

    var viewModel =
    {
        firstName: 'Fred'
    }

and a text box bound to it like this

<input data-bind="value: firstName" >

I was under the impression that this would set up a one way binding as the firstName property is not an observable. Changes to the text box are updating the viewModel object. Can someone explain what is going on and how \ if my assumption is wrong ?

Forby answered 10/9, 2013 at 5:36 Comment(1)
You must be using the knockout-mapping plugin then. All properties on the object being mapped are converted to observables.Request
R
4

The one way binding means : modifications on UI (typing in the input) are 'applied' to the view model. But if you change the viewModel (by code) the UI wont be refreshed.

If you need to refresh the UI by code (js), you have to use an observable.

As you can see in this fiddle, if you click on the 'Change by code' button the view model will be changed but not the UI.

var viewModel = {
    firstName: 'Fred',
    test: function () {
        alert(viewModel.firstName);
    },
    change: function () {
        viewModel.firstName = "new Value";
    }
};

See fiddle.

I hope it helps.

Runoff answered 10/9, 2013 at 5:54 Comment(0)
C
20

If you execute a property OR drop the observable in a knockout binding it becomes a one way/read only. So in the following model for example:

class Person {
  constructor(data) {
    // Method 1: Consume observable using ()
    this.firstName = ko.observable(data.firstName);
    // Method 2: Omit the observable
    this.flatFirstName = data.firstName;
    this.lastName = ko.observable(data.lastName);
    this.fullName = ko.computed(() => `${this.firstName()} ${this.lastName()}`);
  }
}

const me = new Person({
  firstName: "John",
  lastName: "Smith"
});

ko.applyBindings(me);

I can make FirstName a one way/read only property simply by executing it in the binding:

<input data-bind="value: firstName()">
<input data-bind="value: flatFirstName">
<input data-bind="value: lastName">
<label data-bind="text: fullName"></label>

So now the first input only gets the value and can not set it but the second input will have two way binding and will update the LastName property.

Hope this helps.

class Person {
  constructor(data) {
    // Method 1: Consume observable using ()
    this.firstName = ko.observable(data.firstName);
    // Method 2: Omit the observable
    this.flatFirstName = data.firstName;
    this.lastName = ko.observable(data.lastName);
    this.fullName = ko.computed(() => `${this.firstName()} ${this.lastName()}`);
  }
}

const me = new Person({
  firstName: "John",
  lastName: "Smith"
});

ko.applyBindings(me);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input data-bind="value: firstName()">
<input data-bind="value: flatFirstName">
<input data-bind="value: lastName">
<label data-bind="text: fullName"></label>
Capsular answered 10/9, 2013 at 10:22 Comment(0)
R
4

The one way binding means : modifications on UI (typing in the input) are 'applied' to the view model. But if you change the viewModel (by code) the UI wont be refreshed.

If you need to refresh the UI by code (js), you have to use an observable.

As you can see in this fiddle, if you click on the 'Change by code' button the view model will be changed but not the UI.

var viewModel = {
    firstName: 'Fred',
    test: function () {
        alert(viewModel.firstName);
    },
    change: function () {
        viewModel.firstName = "new Value";
    }
};

See fiddle.

I hope it helps.

Runoff answered 10/9, 2013 at 5:54 Comment(0)
F
1

As mentioned, if your property is an observable, executing it will prevent it from being updated when the bound form field changes. If your view model property is not an observable, another hack to prevent form changes being applied to the viewmodel is to bind to an expression instead of directly to the property, like so:

var viewModel =
{
    firstName: 'Fred',
    age: 25,
}

HTML:

<input data-bind="value: (firstName + '')" />
<input data-bind="value: (age + 0)" />
Faris answered 19/2, 2015 at 19:36 Comment(1)
works like a charm you could even just used like that <input data-bind="value: (firstName)" />Spanishamerican

© 2022 - 2024 — McMap. All rights reserved.