Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KnockoutJS and Infinite Loop [duplicate]

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.

like image 312
Dave Avatar asked May 03 '26 19:05

Dave


2 Answers

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:

  • If weight changes, the moment is updated and arm remains fixed.
  • If moment changes, the arm is updated and weight remains fixed.
  • If arm changes, the moment is updated and weight remains fixed.

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.

like image 109
Jeroen Avatar answered May 05 '26 09:05

Jeroen


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.

like image 36
Damien Avatar answered May 05 '26 07:05

Damien



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!