Knockout JS binding "options" is not changing the whole object

Viewed 48

I have following code:

models.js

class MyModel {
    constructor(value, title) {
        this.value = ko.observable(value);
        this.title = ko.observable(title);
    }
}

class MyWrapper {
    constructor(selected_model) {
        this.selected_model = ko.observable(selected_model);
    }
}

view_model.js

class MyVM {
    constructor() {
        let items = [
            new MyModel(1, 'One'),
            new MyModel(2, 'Two'),
            new MyModel(3, 'Three')
        ];

        this.object_to_work_with = ko.observable(new MyWrapper(items[1]));
        this.my_list = ko.observableArray(items);
    }
}

html template

<div data-bind="with: object_to_work_with">
    <select id="my_list" data-bind="options: $root.my_list,
                                    optionsValue: 'value',
                                    optionsText: 'title'
                                    value: selected_model().value"></select>
</div>

And now the issue. At first I thought the value should be selected_model directly:

<div data-bind="with: object_to_work_with">
    <select id="my_list" data-bind="options: $root.my_list,
                                    optionsValue: 'value',
                                    optionsText: 'title'
                                    value: selected_model"></select>
</div>

but this do not work, it causes an error like "undefined has no property 'value'". So I've tried value: selected_model(), now it worked and correct value was selected, but as literal -> if I changed it, object_to_work_with.selected_model() is not changed. And the last try was value: selected_model().value and it seemed to work, but now when I select another value, it does not change object_to_work_with.selected_model(), it changes only the value of selected model, I mean this object_to_work_with.selected_model().value() => <the value I've selected>, but object_to_work_with.selected_model().title() => 'Two' is never changed.

I am searching a solution quite long time and I have no idea how to solve it. According to the manual it looks like it should be value: selected_model, but it causes the exception and I have no idea what am I doing wrong.

Any idea please?

Edit: And moreover the last option value: selected_model().value is changing the value of the item in my_list, because it is of course only reference.

Edit 2: Here I've created a working demo - https://jsfiddle.net/L7tsy4ae/. Just try to use selectbox.

1 Answers

If you decide to go with the optionsValue: 'value' binding, you also have to initialize the selected_model with items[1].value. Instead, I'd opt for not using the optionsValue binding at all so your selection can point to the actual viewmodel.

Here's a working example (with some other other changes not strictly needed: removed the observable wrapper of object_to_work_with and replaced the change event binding with a subscribe inside the viewmodel.)

class MyModel {
    constructor(value, title) {
        this.value = ko.observable(value);
        this.title = ko.observable(title);
    }
}

class MyWrapper {
    constructor(selected_model) {
        this.selected_model = ko.observable(selected_model);
    }
}

class MyVM {
    constructor() {
        let items = [
            new MyModel(1, 'One'),
            new MyModel(2, 'Two'),
            new MyModel(3, 'Three')
        ];

        this.object_to_work_with = new MyWrapper(items[1]);
        this.my_list = ko.observableArray(items);
        
        this.object_to_work_with.selected_model.subscribe(this.print_data, this);
    }
    
    print_data(selected_model) {
        const div = document.createElement('div');
        const text = document.createTextNode(
            `${selected_model.title()}: ${selected_model.value()}`
        );
        
        div.appendChild(text);
        document.getElementById('data').appendChild(div);
    }
}
 
ko.applyBindings(new MyVM());
#content {
  padding: 30px;
  margin: 30px;
  background-color: #e9e9e9;
  border: 1px solid #999999;
  border-radius: 6px;
}

#data {
  border-radius: 6px;
  background-color: #ffffff;
  padding: 20px;
  margin-top: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div id="content" data-bind="with: object_to_work_with">
    <select id="my_list" data-bind="options: $root.my_list,
                                    optionsText: 'title',
                                    value: selected_model"></select>
    <div id="data"></div>
</div>

Related