Bat_Programmer
Bat_Programmer

Reputation: 6851

Two way binding using value set by AJAX - Observable doesn't notify when setting control's value

So, I have a simple page below where the rate field is set by AJAX. However the other calculated fields do not seem to get updated in the view model by KnockoutJS unless I type in the actual value for rate. How can this be resolved?

HTML:

<div>
    Date: <input type="text" data-bind="value: date" id="txtDate" /><br />
    Enter Amount: <input type="text" data-bind="value: amount" /><br />
    Rate: <input type="text" data-bind="value: rate" id="txtRate" /><br />
    VAT: <input type="text" data-bind="value: vat" /><br />
    Total Amount: <input type="text" data-bind="value: totalAmount" />
</div>

JS

 <script type="text/javascript">
    $(function(){
        $('#txtDate').change(function(){
            $.getJSON('/api/Common/GetRate', { param: $(this).val() }).success(function (data) {
                $('#txtRate').val(data);
            });
        });
    });

    function AppViewModel() {
        var self = this;

        self.date = ko.observable('');
        self.amount = ko.observable(0);
        self.rate = ko.observable(0);
        self.vat = ko.pureComputed(function () {
            return self.amount() * 0.1;
        }, this);
        self.totalAmount = ko.pureComputed(function () {
            return self.amount() + self.vat();
        }, this);
    }

    ko.applyBindings(new AppViewModel());

</script>

Upvotes: 0

Views: 648

Answers (1)

JotaBe
JotaBe

Reputation: 39055

You're mixing up jQuery and Knockout, which is always a bad idea.

On the ajax call success, update the observable value, which is the KO way. To do so you need to create the view model before doing the ajax call, and store it in a variable, for example:

var vm = AppViewModel();

Then, on the AJAX call success callback, update the observable value, i.e.:

vm.rate(data);

You should always do it in this way: update the bound value, and the control will reflect the changes. As you've seen trying to do it the other way round is problematic, apart from more difficult.

It doesn't matter if you apply the binding before of after the callback.

And a final note: when you define computed observable the second parameter is to bind the callback function to it. I.e. it will be the value of this inside the function. So, it would make sense to do this:

 self.vat = ko.pureComputed(function () {
      return this.amount() * 0.1;
 }, self);

but what you're doing is pointless. You can omit that second parameter if you don't use this inside the computed function.

NOTE: apart from being a bad idea, the reason why updating the value using jQuery doesn't work is because knockout bindings use events to be able to update observables. When you use jQuery to set the value, no events are triggered, so the observable isn't updated. Please, see the docs for value binding, and pay special attention to valueUpdate parameter description.

Upvotes: 3

Related Questions