简体   繁体   中英

Why is the server getting an empty object when calling Meteor.method with a non-empty object?

How do I call a Meteor method with a object as an argument, if this is at all possible?

Here is what I'm struggling with if it helps

I have the method:

'user.some.update.position'(coords) {
    console.log('method: ', 'user.some.update.position');
    console.log('this.userid: ', this.userId);
    console.log('coords: ', coords);

    check(coords, Object);
    check(coords.latitude, Number);
    check(coords.longitude, Number);
    check(coords.accuracy, Number);

    if (!this.userId)
      throw new Meteor.Error('Not logged in', 'You are not logged in, please log in');

    Meteor.users.update({
      _id: this.userId
    }, {
      $set: {
        coords,
        lastUpdated: new Date()
      }
    });
    return coords;
  }

Which I want to call from the client like this:

> var coords = Geolocation.currentLocation().coords
undefined
> coords
Coordinates {latitude: 58.2441766, longitude: 8.376727899999999, altitude: null, accuracy: 25, altitudeAccuracy: null…}
> Meteor.call('user.some.update.position', coords, function(err, res) {if(err) console.log('err: ', err); if(res) console.log('res: ', res);})
undefined
VM7572:2 err:  errorClass {error: 400, reason: "Match failed", details: undefined, message: "Match failed [400]", errorType: "Meteor.Error"}

But when I do that the server complains that coords is a empty object like this:

method:  user.some.update.position
I20160220-15:49:34.226(1)? this.userid:  nHqj3zaSWExRmqBZq
I20160220-15:49:34.226(1)? currentLocation:  {}
I20160220-15:49:34.227(1)? Exception while invoking method 'user.some.update.position' Error: Match error: Expected number, got undefined
I20160220-15:49:34.227(1)?     at Object.check (packages/check/match.js:33:1)
I20160220-15:49:34.228(1)?     at [object Object]._meteorMeteor.Meteor.methods.user.some.update.position (server/methods/drivers.js:36:5)
I20160220-15:49:34.228(1)?     at packages/check/match.js:103:1
I20160220-15:49:34.228(1)?     at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20160220-15:49:34.228(1)?     at Object.Match._failIfArgumentsAreNotAllChecked (packages/check/match.js:102:1)
I20160220-15:49:34.228(1)?     at maybeAuditArgumentChecks (packages/ddp-server/livedata_server.js:1695:18)
I20160220-15:49:34.228(1)?     at packages/ddp-server/livedata_server.js:708:19
I20160220-15:49:34.228(1)?     at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20160220-15:49:34.228(1)?     at packages/ddp-server/livedata_server.js:706:40
I20160220-15:49:34.229(1)?     at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20160220-15:49:34.229(1)? Sanitized and reported to the client as: Match failed [400]

And the client complains:

err:  errorClass {error: 400, reason: "Match failed", details: undefined, message: "Match failed [400]", errorType: "Meteor.Error"}

Edit: I am using ES6 and object destructuring.

It's clear from your error message that your object has nothing in it. please look at the 4th line of your error message, currentLocation contains no property. Sending a valid object will solve the problem.

what is $set: { coords, supposed to do? you can't do that. You need to take the content of that object apart and put it back together into the $set . Assuming that coords = {lat: 1234, lng: 2345} (or similar), you would do:

$set: {
  lat: coords.lat,
  lng: coords.lng,
  ...

or you can just add it as a sub-object;

$set: {
  coords: coords,

Both the answers by Christian and Faysal contain valid information, I just want to expand on them a little:

The actual exception you're seeing, Error: Match error: Expected number, got undefined , is happening because of this line of code:

check(coords.latitude, Number); .

In your console log, you can see that coords is just an empty object:

I20160220-15:49:34.226(1)? currentLocation: {}

So when your check() method checks the currentLocation for coords.latitude, it throws the exception because coords.latitude is of type undefined, not of type Number like you said it would be in the check() statement.

Once you fix that, Christian pointed out that you'll get another error because of your update statement. MongoDB's $set requires you to pass in an object with the schema { $set: { <field1>: <value1>, ... } } . You are passing in: { $set: {}, lastUpdated: <A valid Date> } . Because that empty object doesn't match the field: value schema $set is expecting, it'll throw an exception. As he says, you'll need to either parse out that object into individual properties or pass it to a property itself.

The object that you are getting from the geolocation request implements the Coordinates interface. You cannot assume anything else about it.

In your case, the object is likely not serializable via EJSON, and therefore cannot be used as a Meteor method call argument as it is.

The method call routine calls EJSON.clone() and given the Coordinates object, it returns an empty object.

> EJSON.clone(coordinates)
Object {}

In Chrome's implementation, I assume that the properties are "owned" deeper in the prototype chain. EJSON relies on underscore's _.keys function when cloning, which in turn lists the object's own properties.

> coordinates.hasOwnProperty("latitude")
false
> "latitude" in coordinates
true

Since creating a custom EJSON type for it seems unrealistic, you can do as @ChristianFritz suggested or use underscore's _.pick .

> _.pick(coordinates, ['latitude', 'longitude', 'accuracy'])
Object {latitude: 12.1212, longitude: 34.3434, accuracy: 50}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM