How to extend knockout observables to read default

2020-02-05 12:40发布

问题:

I have finally found the time to start learning KnockoutJS while building a new MVC4 application. I am trying to figure out the best way to initialize an observable value from a default value that is already set in the view.

This is a contrived example, but I would like to render out a default value for an observable directly to the view as follows:

<input type="hidden" 
       value="@Model.SomeValue"
       data-bind="value: myObservableReference"/>

I know that a default value is normally initialized via:

model.myObservableReference = ko.obervable("SomeValue");

However, I would like to find a way to extend the initialization such that:

model.myObservableReference = ko.obervable();

would read the existing value from the binding if a value exists.

As I have so far managed to keep my KnockoutJS code completely unaware of the razor world, I want to to avoid the sloppiness of:

model.myObservableReference = ko.obervable(@Model.SomeValue);

I am guessing this would be handled via either an extender or custom binder, but if someone could point me in the right direction it would be greatly appreciated.

回答1:

You could try creating your own custom binding handler to achieve this:

ko.bindingHandlers.initializeValue = {
    init: function(element, valueAccessor) {
        valueAccessor()(element.getAttribute('value'));
    },
    update: function(element, valueAccessor) {
        var value = valueAccessor();
        element.setAttribute('value', ko.utils.unwrapObservable(value))
    }
};

<input type="hidden" value="@Model.SomeValue"
       data-bind="initializeValue:myObservableReference, value: myObservableReference"/>


回答2:

For you googlers out there, here is the code to extend the initializeValue binding to a select box as well as input boxes

ko.bindingHandlers.initializeValue = {
    init: function(element, valueAccessor) {
        if (element.getAttribute('value')) {
            valueAccessor()(element.getAttribute('value'));
        } else if (element.tagName === 'SELECT') {
            valueAccessor()(element.options[element.selectedIndex].value);
        }
    },
    update: function(element, valueAccessor) {
        var value = valueAccessor();
        element.setAttribute('value', ko.utils.unwrapObservable(value));
    }
}

NOTE: The initializeValue binding should appear before the value binding in your select's data-bind attribute or it will not work.

<select data-bind='initializeValue: layout_id, value: layout_id, options: layouts, optionsText: "text", optionsValue: "id"'></select>