findOne callback

I have a simple findOne client side:

var item = CourseElements.findOne({
            leafletId: layer._leaflet_id,
            eventId: eventId
        });

        if (!item) {
            console.log("cant see item")
            // the findOne call is too slow
        } else {
           // do stuff, however this never runs as item is undefined
        }

How do I get a callback when the findOne is done and has a result so I can continue running the if statement?

1 Like

This is due to the data not being sent down to the client yet. You’ll need to wait for your subscription to finish.

You cant have callback from it directly.
Better describe what you want to do when it is done and we can help you implement that.

You have to put your code in a computation, like a template helper (best) or an autorun (not so good).
Then when you data is ready (or has changed) your code will be re-run.

1 Like

Thanks copleykj, shock and vjau for the replies.

To give a bit of context, I have a leaflet map. When someone edits the map, I loop through the layers, look for a matching document in the DB and delete the old and insert the new (I don’t update as I have specific observers on insert and remove only)

Here is all the code:

map.on('draw:edited', function(event) {
    var layers = event.layers._layers;
    for (layer in layers) {

        console.log(layer);

        var eventId = FlowRouter.getParam("id");
        var leafletId = layer;

        var item = CourseElements.findOne({
            leafletId: leafletId,
            eventId: eventId
        });
        
        if (!item) {
            console.log("cant find item")

        } else {
            console.log("item found");
            console.log(item);

            switch (item.layerType) {
                case 'marker':
                    item.latlng.lat = layer._latlng.lat;
                    item.latlng.lng = layer._latlng.lng;
                    break;
                case 'polyline':
                    item.latlngs = layer._latlngs;
                    break;
                case 'polygon':
                    item.latlngs = layer._latlngs;
            }

            CourseElements.remove({_id: item._id});
            CourseElements.insert(item);
        }
    };
});

Any ideas how I can await the result of the findOne before moving on? The documents are client side in minimongo already, I can see them in Mongol.

You could wrap your findOne in a method on the server and then invoke the method from the client, using the method callback to carry on with your stuff.

like @serkandurusoy said, you can wrap the findOne in a method and then call the method in client without passing a callback function, then it will be sync call

wrap the findOne in a method and then call the method in client without passing a callback function, then it will be sync call

@seanh that would return undefined since the client is async by default (no fibers)

http://docs.meteor.com/#/full/meteor_call

On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn’t have fibers, so there is not actually any way it can block on the remote execution of a method.

thanks @serkandurusoy for correcting my mistake, I guess I learn something today :smiley:

1 Like

OK, here is my code

map.on('draw:edited', function(event) {
    var layers = event.layers._layers;
    for (layer in layers) {

        console.log(layer);

        var eventId = FlowRouter.getParam("id");
        var leafletId = layer;

        Meteor.call("getCourseElement", leafletId, eventId, function(error, result) {
            if (error) throw new Meteor.Error (error);
            console.log("running result");
            console.log(result);
            if (!result) {
                console.log("there is no result")

            } else {
                console.log("item found");
                console.log(result);

                switch (result.layerType) {
                    case 'marker':
                        result.latlng.lat = layer._latlng.lat;
                        result.latlng.lng = layer._latlng.lng;
                        break;
                    case 'polyline':
                    case 'polygon':
                        result.latlngs = layer._latlngs;
                        break;
                }

                CourseElements.remove({_id: result._id});
                CourseElements.insert(result);
            }

        });
    };
});

and server side:

Meteor.methods({
getCourseElement: function(leafletId, eventId) {
    console.log("getCourseElement running");
    console.log(leafletId + " " + eventId);
    if (!leafletId || !eventId) throw new Meteor.Error("missing fields");
    return CourseElements.findOne({
        leafletId: leafletId,
        eventId: eventId
    });
}
})

Problem is result returns undefined - I have checked on the client manually calling in the console

CourseElements.findOne({
        leafletId: 31,
        eventId: "hN8hHsaN24gSgx9D9"
    });
// Object {_id: "RfpjKdyWmonninzjQ", layerType: "marker", type: "unassigned", order: 1, eventId: "hN8hHsaN24gSgx9D9"…}

and I get the correct object, also in the console:

Meteor.call('getCourseElement', 31, "hN8hHsaN24gSgx9D9", function(error,result) {console.log(result)});
//undefined
// Object {_id: "RfpjKdyWmonninzjQ", layerType: "marker", type: "unassigned", order: 1, eventId: "hN8hHsaN24gSgx9D9"…}

the first thing that comes back is “undefined” and then I get the object - any idea where I am going wrong?

1 Like

When i look at your original code, i don’t see how your findOne could not be sucessful, since you are doing your “manual” replacing after the fact.
Before looking for some complicated workaround involving a meteor method, i would look for the reason why the findOne gives undefined. Is CourseElements defined ? Are leafletId and eventId defined ?
On the client, everything is blocking, you don’t need a callback to get the result of a find, the only reason it could return empty is when the data is not available, ie it has not been published yet.

1 Like

leafletId was a string, not a number as I was pulling it from the leaflet object not the DB :frowning:
Thanks vjau for getting me on the right track.

You are welcome.
I find that most of the time, difficult to track bugs are type related, hence i think that Typescript is a damn good idea and will probably grow in importance in the next years (at least as long as type is not implemented in vanilla javascript).