دنبال کننده ها

۱۳۹۶ شهریور ۱۱, شنبه

javascript - KnockoutJS - two way binding adapter - avoid cycles

[ad_1]



A lot of times in knockout I come at the following situation:



I have an observable, and I want to create a two way binding adapter between the observables A and B, i.e. if A changes, change B and if B changes, then change A.




+-------------+ +-------------+ +-------------+
| A | -----> | Adapter | -----> | B |
| Observable | <----- | | <----- | Observable |
+-------------+ +-------------+ +-------------+


At first this seems like a DON'T DO because this creates a cyclic dependency, BUT in the end this exactly what happens when you bind a GUI element to an observable. Imagine your have an existing binding, but you want to change its binding result, without touching the binding itself.



Lets look at an example (jsfiddle here):



HTML:



<body>
<p data-bind="text: 'Value:' + val()"></p>
<input type="text" data-bind="textInput: val"></input>
<p data-bind="text: 'Value 2:' + val2()"></p>
<input type="text" data-bind="textInput: val2"></input>
</body>


Javascript:



function ViewModel() 
var self = this;

this.val = ko.observable("");
this.val2 = ko.observable("");

this.val.subscribe(function ()
console.log("VAL Changed!");
self.val2(self.val().toUpperCase());
);

this.val2.subscribe(function()
console.log("VAL2 Changed!");
self.val(self.val2().toLowerCase());
);


ko.applyBindings(new ViewModel());


You will notice, that when you type something in the first textbox a cycle is triggered:



  • The binding changes val

  • the subscribe to val fires and changes val2

  • the subscribe to val2 fires and changes val

  • knockout surpresses to run the subscribe for val again (cycle detection)

The result is here that if you type an Uppercase letter in the first input box, it will be immediately converted to a lowercase letter, by the 2nd subscribe and vice versa.



While this seems nice in this example, it can result in very hard to find bugs. A simple way to resolve the problem would now be, to have a flag inside the binding, that will avoid the update when we are inside the update of the other side:



(jsfiddle here)



 ....
var flag = false;
this.val.subscribe(function ()
if (flag) return;
flag = true;
self.val2(self.val().toUpperCase());
flag = false;
);

this.val2.subscribe(function()
if (flag) return;
flag = true;
self.val(self.val2().toLowerCase());
flag = false;
);
....


Now when you change the second input, it will not "fire back" but only fire in one direction.



Now finally my question:



  • Is the adapter an invalid use case and does it hint to a conceptual problem with the code?


  • How would you go about preventing the cylce? With a flag like in my example? Maybe using throtteling?




[ad_2]

لینک منبع