Knockout - write a value to a ko.computed

匿名 (未验证) 提交于 2019-12-03 09:05:37

问题:

I've built a very data/number heavy app in Knockout. I'm currently getting the error:

Uncaught Error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.

This is happening when my custom bindingHandler (which formats the numbers into 'large' form, ie. 123,345,678,987) tries to write back to the original input which displays the value of a computed function.

Value displayed in an input element:

self.value = ko.computed(function(){     return self.chosenAge().population; // 'fetched' from an array. }); 

Binding Handler:

ko.bindingHandlers.largeNumber = {     init: function(element, valueAccessor) {         numberInit(element);         var value = valueAccessor();         var interceptor = ko.computed({             read: function() {                 // inject number formatting                 return numeral(ko.unwrap(value)).format('0,0');             },             write: function(newValue) {                 // remove formatting when writing a new value                 value(numeral().unformat(newValue));             }         });         // display new value in target element         if(element.tagName.toLowerCase() == 'input' ) {             ko.applyBindingsToNode(element, {                 value: interceptor             });         }         else {             ko.applyBindingsToNode(element, {                 text: interceptor             });          }     } }; 

回答1:

You need to specify a 'write' option in your ko.computed function. Please see the documentation on computed observables. Your binding handler has nothing to do with your value failing to update. Your computed should look something like this:

self.value = ko.computed(function(){     read: function () {         return self.chosenAge().population; // 'fetched' from an array.     },     write: function (value) {         //update your self.chosenAge().population value here     },     owner: self }); 

Hope this helps.



回答2:

Personally, I would capture the implementation of your formatting in an extender.

Define an extension that wraps the data observable with a computed formatter:

ko.extenders.largeNumber = function (target, option) {    var formatter = ko.computed( {     read: function(){     var formatted = target().toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");     return formatted;   },     write: function(nv){         var numeric = nv.replace(/,/g,"");          target(numeric);          console.log("View model now contains " + target())   }  })  return formatter; }; 

Then create an observable that uses the extension: (I've wrapped it in a constructor)

function createExtendedObs(initialValue){     return ko.observable(initialValue).extend({     largeNumber: {         viewModel: self     }  }); 

In addition, I would make my observable be the population property within the data object that represents a chosenAge

{   name: "Bronze",   population: createExtendedObs(10000) } 

This is not a requirement. If you want the population to be stored in another observable, I setup the extender to have access to the viewModel through the options parameter. Thus, you would access the other observable that way. If the "other" observable were to be a computed, then you would need to specify the write function (as depicted in the other answer) which is the root of the issue you are having. Another option would be to subscribe to chosenAge and set the "other" population observable from within the subscription.

Using the data property directly, I am able to bind to it as follows,

<div data-bind='with: chosenAge'>     <input data-bind='value: population'></input> </div> 

Check out my fiddle for the full setup.


Edit

Just reviewed a comment about wanting to enter the value "with" the formatting. The field will be formatted each time the computed receives a new value. By default, this will be on focus loss. If you want to modify it as the input is provided then you would need to provide the valueUpdate binding parameter. Since the field is being manipulated dynamically this will cause caret positioning issues. You would need to manage the caret position accordingly.

<input data-bind='value: population, valueUpdate: "afterkeydown"'></input> 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!