I am creating a simple weight and balance application for flying using KnockoutJS. This involves calculating the arm (moment / weight) and the moment (weight * arm). As you can see, arm is dependent on moment, and vice-versa. The issue I'm having is when a user updates either the moment or arm, I get an infinite loop, which I need to break.
Here's my code:
function weightAndBalance(_arm)
{
var self = this;
this.weight = ko.observable(0);
this.arm = ko.computed(function()
{
if(parseFloat(self.weight()) == 0)
{
return _arm;
}
console.log("Arm: " + parseFloat(self.moment()) / parseFloat(self.weight()));
return parseFloat(self.moment()) / parseFloat(self.weight());
});
this.moment = ko.computed(function()
{
console.log("Moment: " + parseFloat(self.weight()) * parseFloat(self.arm()));
return parseFloat(self.weight()) * parseFloat(self.arm());
});
}
ko.applyBindings(new weightAndBalance(80.1));
And my markup:
<table>
<tr>
<th>Item</th>
<th>Weight</th>
<th>Arm</th>
<th>Moment</th>
</tr>
<tr>
<td>Front Passengers</td>
<td><input type="text" data-bind="value: weight" /></td>
<td><input type="text" data-bind="value: arm" /></td>
<td><input type="text" data-bind="value: moment" /></td>
</tr>
</table>
The user should enter a weight, and the moment field should be populated. In the case that the moment field is manually entered, the arm should be automatically recalculated.
I've prepared a live demo in Fiddle form.
I'd suggest making some "private" observables, exposed by computed observables that are also writable.
With three observables depending on eachother, the additional risk for an infinite loop can be prevented by deciding which variable remains "fixed" when one changes.
In this fiddle I've done that as follows:
The "private" observables look like this:
_weight = ko.observable(parseFloat(initialWeight) || 0);
_arm = ko.observable(parseFloat(initialArm) || 0);
_moment = ko.observable(_weight() * _arm());
Then the three computed observables look like this:
self.weight = ko.computed({
write: function (val) {
_weight(parseFloat(val));
_moment(_arm() * _weight());
},
read: _weight
});
self.arm = ko.computed({
write: function (val) {
_arm(parseFloat(val));
_moment(_arm() * _weight());
},
read: _arm
});
self.moment = ko.computed({
write: function(val) {
_moment(parseFloat(val));
_arm(_moment() / _weight());
},
read: _moment
});
Now you can update any one of the three observables safely. Only one of the other observables will change, preventing circularity problems.
You only defined the read method on your computeds. If you want get the typed info you need to add write method.
I create a fiddle in which I create 3 regular observables and 2 methods to process the data. I think it's simpler to do so and it's infinite loop free.
this.weight = ko.observable(0);
this.arm = ko.observable(0);
this.moment = ko.observable(0);
this.computeArm = function () {
if (parseFloat(self.weight()) == 0) {
return _arm;
}
console.log("Arm: " + parseFloat(self.moment()) / parseFloat(self.weight()));
self.arm( parseFloat(self.moment()) / parseFloat(self.weight()));
}
this.computeMoment = function () {
console.log("Moment: " + parseFloat(self.weight()) * parseFloat(self.arm()));
self.moment( parseFloat(self.weight()) * parseFloat(self.arm()));
};
I hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With