Leoj Pyzynski
Leoj Pyzynski

Reputation: 231

Meteor reacts to first change only after second change is made

I've reached the point in my first Meteor app where it's time to move all of my update and inserts over to Meteor.methods on the server side. The first thing that I noticed is that I lost the "instant" reactivity once I did this. Here is what I have:

html

<template name="income">
  {{#each accounts}}
    <tr>
      <td class="row1"><input type="text" maxlength="32" value="{{income_acct_name}}" id="income_acct_name{{_id}}"></td>
      <td class="row2" align="right"><input type="text" size="13" value="{{income_acct_budget}}" id="income_acct_budget{{_id}}"></td>
    </tr>
  {{/each}}
</template>

<template name="cashIn">
  {{#each accounts}}
    <tr>
    <td class="row1"><input type="text" size="18" value="{{cashIn_acct_name}}" id="cashIn_acct_name{{_id}}" readonly></td>
    <td class="row2_protected" align="right">{{cashIn_acct_budget}}</td>
    </tr>
  {{/each}}
</template>

client/income.js

Template.income.events ({
    'change td.row2': function(theEvent, theTemplate) {
        var changedRow = this._id;
        var budget = parseFloat(theTemplate.find('#income_acct_budget'+changedRow).value.replace(/[^\/\d.-]/g,''));
        var incomeCursor = income.find({"_id": changedRow});
            incomeCursor.forEach( function(acct) {
            total = acct.income_acct_total;
        });
        var achieved = 0;
        if (budget > 0)
        {
            achieved = Math.round(total/budget*100);
        }
        Meteor.call("incomeBudgetChange", changedRow, budget, achieved);

        // Update Total Income Budget
        var incomeCursor = income.find({"userID": Meteor.userId()});
        var budgetTotal = 0;
        incomeCursor.forEach( function(acct) {
            budgetTotal = budgetTotal + acct.income_acct_budget;
        });
        var achieved = 0;
        if (budgetTotal > 0)
        {
            achieved = Math.round(incomeTotal/budgetTotal*100);
        }
        Meteor.call('totalIncomeBudgetChange', totalIncomeID, budgetTotal, achieved);
    }
});

server/income.js

Meteor.methods({
    'incomeBudgetChange': function(changedRow, budget, achieved){
        income.update (
            {"_id": changedRow},
            {$set: {"income_acct_budget": budget, "income_budget_achieved": achieved}}
        )
    },
    'totalIncomeBudgetChange': function(totalIncomeID, budgetTotal, achieved){
        cashIn.update (
            {"_id": totalIncomeID},
            {$set: {"cashIn_acct_budget": budgetTotal, "cashIn_budget_achieved": achieved}}
        )
    }
});

The issue here is with totalIncomeBudgetChange. I start out with 10,000 in both {{income_acct_budget}} and {{cashIn_acct_budget}}. I change the value to 15,000 on the income side and the cashIn side is still 10,000. I then change the income side to 20,000 and now the cashIn side is 15,000. cashIn is always one change behind. What happened here when I moved the updating process to the server side? How do I get them to be in sync again?

Upvotes: 0

Views: 43

Answers (1)

saimeunt
saimeunt

Reputation: 22696

The issue with your code right now is that your Meteor.calls are asynchronous but you still consider that they are synchronous : see how after calling the first Meteor.call you immediately start trying to fetch the updated income collection by iterating over a cursor and performing a computation on its supposed updated value, but it's not yet updated !

By moving your collection.update code to the server you understand that you have to perform a round-trip to the server before the client can acknowledge that the collection was indeed modified : this is why client-side Meteor.call are asynchronous most of the time (client-side Meteor.call can be synchronous by providing a method stub when it makes sense to achieve latency compensation).

Your problem is that the code executing immediately after your first Meteor.call assumes that the collection (which is a mini-mongo replica subset of the actual MongoDB server persisted collection) is already updated, but it won't be the case until the server modifications are executed and sent back to the client.

I think you should simply refactor your 2 methods calls into one server-side method call that will perform the updates synchronously as the second method call does not depend on the result of the first one : in general, it does not make sense to perform 2 method calls if the user didn't performed 2 separate actions on their own.

Upvotes: 1

Related Questions