How to get the server data push to the client instead of page refresh?


#1

Hi
I have a server side method which returns different lat lon value for 2 different markers. I need to have a 2 map marker on the client which keeps getting refreshed with the latest lat lon value every time it changes on the server method which gets the latest latlon from an external web service. Can you pls help to implement how i can achieve this using the meteor feature where the server publishes the latest latlon and the browser shows the latest latlon when ever it changes on the server.


Push data from server to the client directly
#2

A simple way would be to have it stored in a collection which is subscribed to by the client (and obviously published by the server). This is pretty much a core principle of what Meteor is good for.
The collection will remain sync’d with all clients who subscribe to that collection.
Clients will automatically be updated to match the server, as long as you allow the client to subscribe.

If the server needs to update the lat long from that external web service, then you will likely need to implement some sort of timed event to fetch that and update the collection. I have never used it but I hear that https://github.com/percolatestudio/meteor-synced-cron is probably what you would need to check the remote web service.

edit - a point I forgot to make here is that you wouldn’t use Meteor call & methods anymore, it would just be taking advantage of the native syncd mongoDB instance across both client & server.


#3

Thanks. My cron job is working. I am also able to publish the data returned by the cron job. However on the client side I am not able to display the data on a label. Can you pls help? All I am trying to do is use the following code in my client JS

Template.layout.helpers({
latlonval: function(){
Session.set(‘latlonval’, Meteor.subscribe(‘latloncollection’));
return Latloncoll.find().fetch();
}
});

Latloncoll.find().fetch() returns the following

[Object_id: “z3thfgjsq66ashb9t” devId: 1 latlonval: "14-10.865676, 78.597565"proto: Object]

I need to get the latlonval and show it on the label. This latlonval keeps changing behind the scenes by the cron job.

and the following code in my html

{{latlonval}}


#4

Your template helper should just return the appropriate object (normally as a cursor):

Template.layout.helpers({
  latlonval: function() {
    return Latloncoll.find();
  }
});

When the collection changes, the helper will re-run reactively. The code shown will return all documents in your collection, which may not be what you want. However, you can filter with find to narrow down the result set.

Your template would look something like:

<template name="latLon">
  <div>
    The latest lat/lon value is: {{latlonval}}
  </div>
</template>

#5

I have made the changes but not luck. I am able to see only [object Object] on the UI. How can i display a particular value i.e latlonval: “14-10.865676, 78.597565”?Also any reason why we are not using the subscribe command and using find instead?


#6

My bad. I should have looked at your object a little more carefully.

So, you have a collection of lat/long values (along with some other stuff).

We start with a template to cycle through the documents:

<body>
  {{> allLatLongDocs}}
</body>

<template name="allLatLongDocs">
  {{#each latLongDocs}}
    <div>
      The latest lat/lon value is: {{latlonval}}
    </div>
  {{/each}}
</template>

The template helper:

Template.allLatLongDocs.helpers({
  latLongDocs: function() {
    return Latloncoll.find();
  }
});

That should fix the issue of presenting your values reactively.

Using a subscription is still a good thing to do, but it’s not done in the way you’d originally written.


#7

Thanks. Pls note I have always only one item in the collection in this example as I am removing the collection and then doing an insert in the server.js.
I have tried few changes suggested by you and now I am not able to get any value returned from Latloncoll.Find() and its returning empty. From the server side i am inserting and publishing the values which i can see new values being published to the mongo Db every 5 sec using cron job. But i am not able to get it in the client. Also what is the different in using the Find() and using subscribe? and which one is better?

Pls help!!

My common.js has the below collection definition.

Latloncoll = new Mongo.Collection(“latloncollection”);
My server.js code is as follows

Meteor.methods({
‘GetRandomLatLon’:function(someId){
if(someId == 1){
var x = Math.floor((Math.random() * 10) + 1);
var latlon = [‘11-10.536421, 78.787079’,‘12-10.647112, 78.740387’,‘13-10.755064, 78.712921’,‘14-10.865676, 78.597565’,‘15-10.911527,78.449249’,‘16-10.927708, 78.240509’,‘17-10.755064, 78.712921’,‘18-10.865676, 78.597565’,‘19-10.911527,78.449249’,‘20-10.927708, 78.240509’];
console.log(latlon[x]);
Latloncoll.remove({});
Latloncoll.insert({
devId:someId,
latlonval: latlon[x]
});
return latlon[x];
}
else if(someId==2){
var y = Math.floor((Math.random() * 10) + 1);
var latlon1 = [‘21-10.404079, 79.775848’,‘22-10.463505, 79.696198’,‘23-10.533720, 79.646759’,‘24-10.576922, 79.602814’,‘25-10.641713, 79.509430’,‘26-10.746969, 79.438019’,‘27-10.533720, 79.646759’,‘28-10.576922, 79.602814’,‘29-10.641713, 79.509430’,‘30-10.746969, 79.438019’];
console.log(latlon[y]);
Latloncoll.insert({
devId:someId,
latlonval: latlon[y]
});
return latlon1[y];
}
},

'setCron':function(){
    SyncedCron.add({
        name: 'Query the scheduled emails and send if necessary.',
        schedule: function(parser) {
            return parser.text('every 5 sec');  /*later.parse.recur().every(15).minute().startingOn(10);*/
        },
        job: function() {
            return Meteor.call('GetRandomLatLon',1);
        }
    });
}

});

Startup.js

Meteor.startup(function () {

 Meteor.call('setCron');

SyncedCron.start();
Meteor.publish('latloncollection',function(){
    return Latloncoll.find({});
});

});


#8

I think, you only need to publish this markers. On client you have to subscribe to this collections of markers and use cursor.observe for markers actions: if smth was changed in collection, you have to remove old markers and draw new.

It’s easy, I do the same (-:


#9

sorry I am unable to follow. Can you pls give an example to help me understand this?
Thanks


#10

In my case I have a map and markers
I publish markers.

Meteor.publish('markers', function() {
  this.unblock();
  return Markers.find({});
});

On client I make subscription in iron-router

 waitOn: function(){
     return Meteor.subscribe("markers");
 }

And then

Template.someYourTemplate.rendered = function () {
     ....
  if(markersObserveHandler) {
 	markersObserveHandler.stop();
  }

  markersObserveHandler = Markers.find({}).observe({
 	added: function (marker) {
                //do smth when added new marker
        },
        removed: function (oldMarker) {
                //some logic for removing
        },
        changed: function(newMarker, oldMarker) {
                //here you put your logic, when smth changed: remove old marker and draw new, make some animation from old position to new, etc.
        }

#11

I am implementing this and adding some code under the changed event.

All I want to do is subscribe to the latloncollection which has been published and show the latlonval on the screen as a marker. I have written a dummy function GetRandomLatlon to simulate different latlon values and publish it to the mongo db. What code should i write to fetch the latlonval which has been published and assign to the marker on the map? I am using leaflet package for the map.

Pls help.


#12

It’s depends on your collection structure.
In my case I have field

location: {
    type: "Point",
    coordinates: [Lng, Lat]
}

It’s MongoDB datatype for geo queries.
Then on server in publication:

Markers.find({}, fields: {location: 1})

Then in cursor.observe:

markersObserveHandler = Markers.find({}).observe({
    added: function (marker) {
            //smth like this
       var lat = marker.location.coordinates[1];      
       var lng = marker.location.coordinates[0];
       var marker = L.marker([lat, lng]).addTo(map);
    }

Look at the small example localgeo and code of this. Don’t try to run this example, because it’s old version of Meteor and packages. But you can see, how observe works.


#13

does Markers.find({}, fields: {location: 1}) return the coordinates: [Lng, Lat]? Also what is markerObserveHandler refers to i.e do i need to just declare it as a var ? Also why is it referring to only the added event and not the updated event? Since my collection is like this

{ “_id” : “qPsJsrQsLQ6ceL6oZ”, “devId” : 1, “latlonval” : “18-10.865676, 78.597565”} where for one devId there can be only one latlonval so I need to publish all the content and not just the latlon value.

But when I subscribe to a latlonval I need to find the specific latlonval for a devId.


#14

No, it returns location completly, with type and coordinates
If you need only coordinates in subscription, then try this Markers.find({}, fields: {'location.coordinates': 1}) in publication.

And most important: Collection.find() always returns cursor, not array, not Object, etc. Then you can use fetch or map, if you don’t like observers.


#15

what is markerObserveHandler refers to i.e do i need to just declare it as a var ? Also why is it referring to only the added event and not the updated event? Since my collection is like this

{ “_id” : “qPsJsrQsLQ6ceL6oZ”, “devId” : 1, “latlonval” : “18-10.865676, 78.597565”} where for one devId there can be only one latlonval so I need to publish all the content and not just the latlon value.

But when I subscribe to a latlonval I need to find the specific latlonval for a devId.


#16

The latlonval for one unique devId will keep changing using the GetRandomLatLon method which i have mentioned above. So I am not able to relate this to the addded event instead would have thought to be on the changed event? Am i missing anything?

Sorry if i am asking some stupid questions. I am new to meteor and showing a latlon value on the screen seems to be a night mare using publish and subscribe. My end goal is to show multiple markers on the OSM map using leaflet whos latlon value will keep changing and i need to see all the marker positions changing based on the latlon for each marker(devId is the marker Id in this case).


#17

I am not sure why a waitOn is required as suggested by you. I have implemented some thing like this

Router.map(function(){
this.route(‘home’,{
path:’/’,
template: ‘layout’,
waitOn: function() {
return Meteor.subscribe(‘latloncollection’)}
});
this.route(‘modules’,{path:’/modules’});
this.route(‘tracking’,{path:’/tracking’});
})

I thought meteor will automatically subscribe to a collection and get the new values when ever there is a change. Can you pls help understand the need for a waitOn ? From my requirement perspective, the marker position will change when ever a new latlon value is published by the server. If there is no change in the latlon value then the marker will remain static.


#18

Because example shows minimongo posibility to make geo queries on client side.
Yes, you can make it local variable, you can add removed and/or changed callback, I didn’t need it in example.
If you need all yours data, then publish all: Markers.find({}), no problem. But your latlonval not a good type, because you have to parse this string for get Lat and Lng, use Object or Array.
Pub/Sub with observer is a right way, IMHO. It doesn’t matter, in wich way coordinates changed. Only what you have to do, it make some logics in changed callback of observer: remove marker in old position and draw in new.


#19

Normally, publish is used to provide a subset of documents in a collection. That subset may be for reasons of security (e.g. you may need to be logged in to see them), for performance (e.g. your client only needs the top 10 posts, not all 1000000 that are actually in the collection), or both. Alternatively, you can publish arbitrary data, not in a server-side database collection at all.

You would normally use a server-side database collection when you want persisted data (e.g. the classic collection of blog posts). For transient data which doesn’t need persisting you can avoid the overhead of a disk-backed collection entirely (e.g. you are collecting data from a weather station and all you want to know is the wind speed right now).

The way your code is currently written suggests that with a someId of 2, multiple documents will be written to your collection, which is not what you have said is the way it’s supposed to work (remove/insert).

In any event, if you have published on the server (which is recommended practice), you will need to subscribe on the client to see data there. However, the data is just made available in your collection and you would normally then find() to define the cursor for rendering in Blaze. This behaviour is standard Meteor and is fully reactive.

At the moment I’m unsure of what you’re trying to achieve, how to resolve the differences between explanation and code, and how to best help you get where you want to be.


#20

Hi Rob,
My end goal is to have a OSM map which shows multiple markers(using leaflet) and these markers will get their current lat lon position from an API which doesnt exit as of now so I am mocking what the api is supposed to do by defining a GetRandomlatlon method. On the screen i should be seeing all the different markers moving automatically depending upon the lat lon value returned by the api/getRandomLatlon method.
Is this good enough information to let you know my end goal?