0

I'm working hard on a meteor App which goal is to dynamically display on a google map the path of a vehicle, for example a boat on the sea.

Now, I see this library called gmaps.js, and since it is available on npm (just like google maps api) I decide to use this as a solution to draw of the map.

So, I have one page that add a geographic position (/runs/send) in the database each time I click on a button (this is enougth for testing). Then, on my other page (/runs/show) the goal is to get that data from mongo and prompt it dynamically on the map (meaning, if I add data by pressing the button, I'll see the new path appear on the map). Here is what the code looks like for now :

import { Template } from 'meteor/templating';
import {Tracker} from 'meteor/tracker';
import { Meteor } from 'meteor/meteor'
import gmaps from 'gmaps';
import google_maps from 'google-maps';

import {Mongo} from 'meteor/mongo';

 import {Run} from './run.js';
 import './methods.js';

Template.runs_show.helpers({
    all() {
        return Run.find({});
    }
});

Template.runs_show.onCreated(function() {

    var self = this;
    self.autorun(function() {
        self.subscribe('allRuns',function() {
            google_maps.KEY = "MY API KEY";
            google_maps.load(function(google){

                console.log("Google Maps loaded");

                // this creates a new map
                var map = new gmaps({
                      el: '#map-gmaps',
                      lat: -12.043333,
                      lng: -77.028333
                });

              // for now , the data is on the run with {"maxSpeed" : "75"}
              var dada = Run.findOne({"maxSpeed" : "75"});

              // path look like [[x1,y1],[x2,y2]]
              var path = dada.positions;

              // this draws a red line on the map following the points defined in path
              map.drawPolyline({
                  path: path,
                  strokeColor: '#FC291C',
                  strokeOpacity: 0.85,
                  strokeWeight: 6
              });


              });
        });
    });

  });

So, as you can see, I put my onCreated function in a autorun block, and the data i'm using is from a database, so it's a cursor, so it should be reactive as well.

With a reactive data, inside a reactive block of code (thanks autorun)? expected to see a new line appear on my screen when I press "send" in my second page (this page just add a new set of [x,y] to the run.positions), but.... Nothing ! In fact, If I reload the page manually, the new line appears, of course, but wellll... That's not what I wanted to be honest...

So that's it! any idea what is missing in order to have some true reactivity?

EDIT :

This code works partially : the first time I load the page, the console.log(map) gives a undefined, but I just need to reload once, and then the page will work exactly as intended, showing what I want dynamically. However, one single code reload, and then, again, the console.log(map) gives undefined, and I need a new F5.... Any idea on why it does that / how to solve it?

Template.runs_show.onCreated(function() {

        google_maps.KEY = "MY API KEY";
        google_maps.load(function(google){
            var map = new gmaps({
                  el: '#map-gmaps',
                  lat: -12.043333,
                  lng: -77.028333
            });

            // with that, I can use the map in the onRendered
            Template.runs_show.map = map;
        });

        console.log(Template.runs_show);

 });

 Template.runs_show.onRendered(function() {

     var self = this;
     self.autorun(function() {
         self.subscribe('allRuns',function() {
             Tracker.autorun(function(){

                 var map = Template.runs_show.map;

                 console.log(map);

                 var dada = Run.findOne({"maxSpeed" : "75"});
                 var path = dada.positions;

                 map.drawPolyline({
                     path: path,
                     strokeColor: '#FC291C',
                     strokeOpacity: 0.85,
                     strokeWeight: 6
                 });

                  // seems to not be necesary
                 //map.refresh();

             });
        });
    });

 });

(in this new code, I just create the map in the onCreated, when the gmaps is loaded, and then, I make all the drawing in the onRendered. Btw, I used Template.xxx.map to transmit data between onCreated and onRendered, is that what i'm supposed to do?)

xababafr
  • 5
  • 4
  • Try using `Tracker.autorun` instead of `self.autorun` or adding `.bind(this)` to the end of `self.autorun(function(){..}.bind(this));` – mutdmour Mar 14 '17 at 21:48
  • What is the purpose of the .bind? – xababafr Mar 14 '17 at 22:02
  • about managing the scope or context of `this`. It binds `this` to the `Template` instead of the callback. [example](https://stackoverflow.com/questions/35000844/meteor-template-autorun-is-not-a-function-when-using-es6) – mutdmour Mar 14 '17 at 22:08
  • I kinda see... Sadly, tracker.autorun and .bind both failed... Do you think there's any chance it comes from how gmaps.js works? – xababafr Mar 14 '17 at 22:09
  • could be, though I doubt it. I am looking at the `autorun` implementation and there's restrictions about how to use it. Try doing what worked for this guy [here](https://forums.meteor.com/t/template-subscriptions-and-autorun/4699/23?u=mutdmour) and [here](https://stackoverflow.com/questions/29443513/meteor-tracker-autorun-observechanges-collections-not-working-as-expected). – mutdmour Mar 14 '17 at 22:18
  • also have you tried doing this in `onRendered`? – mutdmour Mar 14 '17 at 22:18
  • Okay, so, I tried in a new way, and now it kinda works... I just need to refresh the page once, and then everything works fine.... ? Any ideas? The new code and explanations on what works / doesnt are in the **EDIT** of my question – xababafr Mar 15 '17 at 07:22
  • Why are you splitting the code between oncreated and onrendered ? Can you initialize the map in onrendered? – mutdmour Mar 15 '17 at 16:36
  • I tried splitting, it doesnt change a thing, apart maybe the map "freezes" a bit, so, I prefer the other way around... – xababafr Mar 15 '17 at 18:45
  • I guess I don't really understand what you mean by reload? When is it not working? When you refresh the page? Is the `Tracker.autorun` running at first refresh? What are you 'reloading' to make it work? – mutdmour Mar 15 '17 at 18:51
  • So. When I launch my meteor command and go on the /runs/show page for the first time, the console.log(map), which is in the tracker.autorun, just says that map is "undefined" and therefore, the whole code doesnt work. But then, I just need to refresh the page (I mean, a true refresh, like a F5,or you just type the url and hit enter once more) and the exact same code will show me the map object, and then the whole code works. And If I refresh again, it'll still working. The only way to get undefined again is by making a code change , or to quit meteor and relaunch the meteor command.Clearer? ^^ – xababafr Mar 15 '17 at 20:04
  • then the issue is probably with how you are loading the google maps library. try wrapping it in a package or find if someone already did that and published it. – mutdmour Mar 15 '17 at 20:33
  • since the tracking issue is solved, I am gonna post it as an answer that you can accept so that others who are looking for an answer to the same question can get the same advice. – mutdmour Mar 15 '17 at 20:38
  • the thing is : gmaps.js, i load it with npm already (look at the top of the code, how i import it) , so..... (yes, i'll acccept the answer as you found the first bug, so thanks on that :) if you have any idea on the following, it would be great :) ) – xababafr Mar 15 '17 at 21:42
  • Thanks. what does a `console.log(Template.runs_show.map);` at the end of `onCreated` return in the `undefined` case (when you are loading meteor the first time)? – mutdmour Mar 15 '17 at 21:48
  • Ok.... so, for a reason, and I have no idea why, it works... even on the first try.... So, I'lll just consider this as resolved for now, but maybe i'll repost a comment here ^^' let's hope not, and thanks for your help :) – xababafr Mar 15 '17 at 22:44

2 Answers2

0

Seems the issue is that the subscribe callback is not a reactive context. Try doing what worked for others here and here, as well as putting the tracking in your onRendered.

 Template.templateName.onRendered(function() {
     var self = this;
     self.autorun(function() {
         self.subscribe('allRuns',function() {
             Tracker.autorun(function(){
                 ...
             }
         })
     })
 })
mutdmour
  • 543
  • 5
  • 14
0

Try using nested templates for this. So that your wrapper template subscribes to data and only renders nested template when subscriptions is ready:

//wrapper template that manages subscription
Template.wrapper.onCreated(function() {
  this.subscribe('allRuns');
});

Template.runs_show.onRendered(function() {
  google_maps.KEY = "MY API KEY";
  google_maps.load(function(google){
    var map = new gmaps({
      el: '#map-gmaps',
      lat: -12.043333,
      lng: -77.028333
    });

    Tracker.autorun(function() {
      // Run.find will re-trigger this whole autorun block
      // if any of the elements of the collection gets changed or
      // element gets created or deleted from collection
      // thus making this block reactive to data changes
      // this will also work with findOne in case you only want to
      // one run only
      var runs = Run.find({"maxSpeed" : "75"}).fetch();
      // map.clear() or something like that to remove all polylines
      // before re-rendering them
      runs.forEach(function(run){
        map.drawPolyline({
          path          : path,
          strokeColor   : '#FC291C',
          strokeOpacity : 0.85,
          strokeWeight  : 6
        });
      });

    });
    // ^^ this is pretty dumb and re-renders all items every time
    // something more intelligent will only re-render actually 
    // changed items and don't touch those who didn't change but
    // that will require a slightly more complex logic

  });
});

wrapper.html:

<template name="wrapper">
  {{#if Template.subscriptionsReady }}
    {{> runs_show }}
  {{/if}}
</template>

P.S. this is mostly pseudo code since I never tested it, so just use it as a guide

Pavlo Sirous
  • 161
  • 3
  • Okay, I just saw your answer, and it's very interesting... Thanks for putting some efforts to help me, I appreciate. I cannot test it right now, but in few days i'll be able to and then i'll tell u if anything goes better this way. Anyway, i'd probably never thougth about that, so, good idea :p – xababafr Mar 19 '17 at 23:50
  • Happy that it helped! @xababafr – Pavlo Sirous Mar 21 '17 at 19:41