How to implement checkbox dependencies in knockoutjs

I have a group of flags

  • checkbox A
  • checkbox B
  • flag C

Generated with foreach data binding:

<input type="checkbox" data-bind="value: id, checked: $root.chkboxSelected" /> 

which take their controlled state from the observed array. Thus, when checking the field, the corresponding value is added to the array, the standard class knockoutjs, which works fine. Then I wanted to add a simple rule:

if C is checked, then it is also necessary to check A and B.

What is the cleanest way to add this logic to a knockout? I tried with a write ability that can be calculated:

 var viewModel = { foo: observableArray(), .. }; viewModel.chkboxSelected = ko.computed({ read: function() { return this.foo(); }, write: function(value){ //add it if not added already if($.inArray(value, this.foo()) < 0) { this.foo.push(value); } // if C is present then A,B must be as well if($.inArray("C", this.foo()) >= 0) { if($.inArray("B", this.foo()) < 0) { this.foo().push("B"); } if($.inArray("A", this.foo()) < 0) { this.foo().push("A"); } } }, owner: viewModel }); 

Putting a breakpoint on the read and write functions: reading calls and the page loads fine. However, when I then select any checkbox, I get the following error (the record breakpoint never hits):

 knockout-2.0.0.debug.js:2297 Uncaught TypeError: Object function dependentObservable() { if (arguments.length > 0) { if (typeof options["write"] === "function") { // Writing a value var valueForThis = options["owner"] || evaluatorFunctionTarget; // If undefined, it will default to "window" by convention. This might change in the future. options["write"].apply(valueForThis, arguments); } else { throw "Cannot write a value to a dependentObservable unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters."; } } else { // Reading the value if (!_hasBeenEvaluated) evaluateImmediate(); ko.dependencyDetection.registerDependency(dependentObservable); return _latestValue; } } has no method 'push' 
+6
source share
1 answer

When the checked binding is bound to an array, then it should be able to perform array operations against it. Thus, using the record calculated in this case may cause a problem.

However, you can use manual subscription to sync your items.

Here is an example view model:

 var ViewModel = function() { var self = this; this.items = ko.observableArray([ { id: "A" }, { id: "B" }, { id: "C" }, { id: "D" } ]); this.checked = ko.observableArray(); this.checked.subscribe(function(newValue) { if (self.checked.indexOf("C") > -1) { if (self.checked.indexOf("A") < 0) { self.checked.push("A"); } if (self.checked.indexOf("B") < 0) { self.checked.push("B"); } } }); this.shouldBeDisabled = function(item) { return (item.id === "B" || item.id ==="A") && self.checked.indexOf("C") > -1; }; }; 

Here is a view:

 <ul data-bind="foreach: items"> <li> <span data-bind="text: id"></span> <input type="checkbox" data-bind="attr: { value: id }, checked: $root.checked, disable: $root.shouldBeDisabled($data)" /> </li> </ul> 

I used attr: { value: id } instead of value to avoid the event handler, which will be bound by the value binding, since the value binding is designed to handle changes to the field. In this case, we only want to set the value attribute.

Example here: http://jsfiddle.net/rniemeyer/tQJMg/

+9
source

Source: https://habr.com/ru/post/908146/


All Articles