Callback in Meteor.call() not working? [ViewModel]


#1

Hi all!

I have the following method call in my client side helper using manuel:viewmodel:

Template.ItemCapture.viewmodel({
  [...]
  saveItem: function() {

    item = { [...]}

    Meteor.call('addItem', item, Meteor.userId(), function(error, result) {

      console.log('Check1');

      if (error) {
        return alert(error.reason);
      }

      Router.go('item.detail', {
        _id: result._id
      });

    });

  },
    
  [...]

});

The item passed to the addItem method gets saved as it should be (i.e., addItem is being executed as intended on the server side), but none of the code in the callback appears to be run.

What might be the cause of this? I read that it might be an issue around (a)synchronicity, but I can’t make much of that at the moment.

TIA
Matt

NB: This should be an almost exact copy from Commit 7-7 in “Discover Meteor”, so I am a bit at a loos why this wouldn’t be working.


#2

What does the method addItem look like? What parameters does it expect?

BTW, the server method knows who the this.userId is. Adding the userId to the parameters of the method makes it inherently insecure, because now I can call addItem from the client side browser console with some arbitrary other userid


#3

Here’s addItem:

addItem: function(itemSubmitted, currentUserId) {

    // validate input
    check(currentUserId, String);
    check(itemSubmitted, {
      text: String,
      language: String,
      tags: String
    });
    
    // split tags into array
    itemSubmitted.tags = itemSubmitted.tags.split(',');

    // add additional properties
    item = _.extend(itemSubmitted, {
      createdAt: new Date(),
      addedBy: currentUserId,
      status: 'submitted'
    });

    // store item
    var itemId = Items.insert(item);

    console.log('Item inserted with ID ', itemId); // this is written to console

    // return ID of stored item
    return {
       _id: itemId 
    };
    
  },

Thanks for pointing out the issue relating to Meteor.userId(). Will change everything to use it server-side only.


#4

Looks like you return an object {_id: itemId} from your method, but in the Method callback you treat the result as a string?


#5

Sorry, corrected the Router.go() call in my initial post:

     Router.go('item.detail', {
         _id: result._id
     });

#6

The key issue appears to me that it seems like the callback is not executed at all. I do not get the output expected from the

console.log('Check1');

written to the console.


#7

I am not too familiar with manuel:viewmodel. Can you try to reproduce this in a standard Blaze template event callback?


#8

Are you absolutely certain your method is on the server? What you are seeing suggests that the method is being executed as a client stub.

…On the client, the return value of a stub is ignored. Stubs are run for their side-effects: they are intended to simulate the result of what the server’s method will do, but without waiting for the round trip delay…

Your Items.insert(item) would still work if the insecure package was present, or you have appropriate allow rules for that user.


#9

The method is in server/lib/itemLib.js. I had already removed insecure.


#10

I now tried this with what should be close to the “standard Blaze template” approach that @jamgold suggested - and it’s working:

Template.ItemCapture.events({
  'submit form': function(e) {
    e.preventDefault();

    item = { [...] }

    Meteor.call('additem', item, function(
      error, result) {

      if (error) {
        return alert(error.reason);
      }

      FlashMessages.sendSuccess("Your item was submitted.");

      Router.go('item.detail', {
        _id: result._id
      });

    });

  }
});

So it does look like ViewModel is causing a different behavior. Anyone with an idea why?


#11

On the surface it doesn’t look like a ViewModel problem. Take the following code and start adding things one at a time until you hit the error:

<body>
  <button {{b "click: callServer" }}>Call Server</button>
</body>
if (Meteor.isClient) {
  Template.body.viewmodel({
    callServer: function () {
      Meteor.call("getMessage", function(err, message){
        if (err) {
          alert( err.reason );
        } else {
          alert( message );
        }
      })
    }
  });
}

if (Meteor.isServer) {
  Meteor.methods({
    getMessage: function(){
      return "Hello World";
    }
  });
}