0

I'm totally new to Javascript from Java, and I have had to start directly for the most difficult thing for me... developing an app using the Asynchronous Module Definition pattern...

I'm using the platform Weejot, which provides a framework to develop javascript web apps that uses AMD pattern. All documentation here. You probably don't know it, but maybe you can help anyway if you know the AMD pattern...

Basically I have a module MyController.js that looks like this (note that most of the code is automatically generated, I only wrote the commented lines):

define([
    "js/util",
    "framework/Controller"
], function (
  util,
  Controller
){      
  function MyController(environment, connector) {
    Controller.call(this, environment);
    this.settings = environment.siteApplication.getSettings();
    this.connector = connector;        
    //Variable where I have some value stored
    this.myVariable = "hello";
  }    

  MyController.prototype = Object.create(Controller.prototype);

  util.extend(MyController.prototype, {
    //Function to show a map
    showMap: function (request, render) {
      //Get google maps script
      $.getScript("http://maps.googleapis.com/maps/api/js?key=KEY&sensor=false");          
      //Render is a framework object that basically shows the view "map.html" in full page mode
      render({name: "map", mode: "fullPage"});
    }    
  });

  return MyController;
});

This doesn't work fine, because after the line of $.getScript(...), the script doesn't seem to be loaded, since I can't access the object google.maps, which should have been created...

I made it working by adding a callback function to the script url, like this:

$.getScript("http://maps.googleapis.com/maps/api/js?key=KEY&sensor=false&callback=drawMap");

The problem is that this callback function must be a global function, so I added the following function to the previous module (just before the return statement above):

window.drawMap = function () {
  var mapOptions = {
    center: new google.maps.LatLng(-34.397, 150.644);,
    zoom: 8,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };
  var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
}

And somehow, I get the map rendered perfectly... BUT, my problem is that I need to access the variable myVariable from the function window.drawMap, and I can't... is there any way to do it? or I just doing everything wrong from the beginning?

As I said I'm new to all this, and I'm getting crazy and in my try to make it working I've been adding and changing things to the point that I don't know what I'm actually doing... Any suggestion will be appreciated...


EDIT: if it's useful, this is the response when getting the Google Maps script (copied from Chrome's console):

window.google = window.google || {};
google.maps = google.maps || {};
(function() {

  function getScript(src) {
    var s = document.createElement('script');

    s.src = src;
    document.body.appendChild(s);
  }

  var modules = google.maps.modules = {};
  google.maps.__gjsload__ = function(name, text) {
    modules[name] = text;
  };

  google.maps.Load = function(apiLoad) {
    delete google.maps.Load;
    apiLoad([0.009999999776482582,[[["http://mt0.googleapis.com/vt?lyrs=m@216000000\u0026src=api\u0026hl=es-ES\u0026","http://mt1.googleapis.com/vt?lyrs=m@216000000\u0026src=api\u0026hl=es-ES\u0026"],null,null,null,null,"m@216000000"],[["http://khm0.googleapis.com/kh?v=128\u0026hl=es-ES\u0026","http://khm1.googleapis.com/kh?v=128\u0026hl=es-ES\u0026"],null,null,null,1,"128"],[["http://mt0.googleapis.com/vt?lyrs=h@216000000\u0026src=api\u0026hl=es-ES\u0026","http://mt1.googleapis.com/vt?lyrs=h@216000000\u0026src=api\u0026hl=es-ES\u0026"],null,null,"imgtp=png32\u0026",null,"h@216000000"],[["http://mt0.googleapis.com/vt?lyrs=t@131,r@216000000\u0026src=api\u0026hl=es-ES\u0026","http://mt1.googleapis.com/vt?lyrs=t@131,r@216000000\u0026src=api\u0026hl=es-ES\u0026"],null,null,null,null,"t@131,r@216000000"],null,null,[["http://cbk0.googleapis.com/cbk?","http://cbk1.googleapis.com/cbk?"]],[["http://khm0.googleapis.com/kh?v=75\u0026hl=es-ES\u0026","http://khm1.googleapis.com/kh?v=75\u0026hl=es-ES\u0026"],null,null,null,null,"75"],[["http://mt0.googleapis.com/mapslt?hl=es-ES\u0026","http://mt1.googleapis.com/mapslt?hl=es-ES\u0026"]],[["http://mt0.googleapis.com/mapslt/ft?hl=es-ES\u0026","http://mt1.googleapis.com/mapslt/ft?hl=es-ES\u0026"]],[["http://mt0.googleapis.com/vt?hl=es-ES\u0026","http://mt1.googleapis.com/vt?hl=es-ES\u0026"]],[["http://mt0.googleapis.com/mapslt/loom?hl=es-ES\u0026","http://mt1.googleapis.com/mapslt/loom?hl=es-ES\u0026"]],[["https://mts0.googleapis.com/mapslt?hl=es-ES\u0026","https://mts1.googleapis.com/mapslt?hl=es-ES\u0026"]],[["https://mts0.googleapis.com/mapslt/ft?hl=es-ES\u0026","https://mts1.googleapis.com/mapslt/ft?hl=es-ES\u0026"]]],["es-ES","US",null,0,null,null,"http://maps.gstatic.com/mapfiles/","http://csi.gstatic.com","https://maps.googleapis.com","http://maps.googleapis.com"],["http://maps.gstatic.com/intl/es_es/mapfiles/api-3/12/11","3.12.11"],[2244818506],1.0,null,null,null,null,0,"blabla",null,null,0,"http://khm.googleapis.com/mz?v=128\u0026","AIzaSyAgUZ1LkdjZ9jsRivdSB4cDLmUAOMOMi34","https://earthbuilder.googleapis.com","https://earthbuilder.googleapis.com",null,"http://mt.googleapis.com/vt/icon"], loadScriptTime);
  };
  var loadScriptTime = (new Date).getTime();
  getScript("http://maps.gstatic.com/intl/es_es/mapfiles/api-3/12/11/main.js");
})();
MikO
  • 18,243
  • 12
  • 77
  • 109

1 Answers1

1

EDIT: I looked at the script, and its not jsonp, so I modified my answer to use requirejs.

EDIT again: It appears that there is some more asychronous jazz going on here. Another edit to bind the global function to the module scope.

Looking at the code returned by that url, it appears that within the code it makes another asynchronous call to retrieve additional data from the api, and then calls your callback. The only way I can think to do it is to define a global function within your module and use that to handle the return value from the api call. Its not pretty, but it should work:

define([
    "js/util",
    "framework/Controller"
], function (
  util,
  Controller
){      
  function MyController(environment, connector) {
    Controller.call(this, environment);
    this.settings = environment.siteApplication.getSettings();
    this.connector = connector;        
    //Variable where I have some value stored
    this.myVariable = "hello";
  }    

  MyController.prototype = Object.create(Controller.prototype);

  util.extend(MyController.prototype, {
    //Function to show a map
    createRenderer : function(){
       var self = this;
       window.drawMap = function(){
          var mapOptions = {
          center: new google.maps.LatLng(-34.397, 150.644);,
          zoom: 8,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
        // Do stuff with self.myVariable and, render, ect...
      };
    },
    showMap: function (request, render) {
      //Get google maps script
      this.createRenderer();
      $.getScript("http://maps.googleapis.com/maps/api/js?key=KEY&sensor=false&callback=drawMap");                     }    
  });

  return MyController;
});

Explanation:

When you call this.createRenderer(), the function is called in the context of your object. Since we declare a variable, self = this, within the this.createRenderer() function call, then define window.drawMap within that function call as well, we create a closure, which stores the self reference to the MyController object. When window.drawMap is called later, the self reference is available in the closure scope. So now, within the window.drawMap function call, we can access self.myVariable. See How do JavaScript closures work? for more information.

As an alternative, you could consider using Miller Medeiros's async plugin to load the Google Maps api as a dependency to your module. This would be a much cleaner way to do it.

Community
  • 1
  • 1
Ryan Lynch
  • 7,676
  • 1
  • 24
  • 33
  • Thanks for your help @Ryan, but I don't know why, inside the require (where you wrote `//Do stuff with self.myVariable`), I can access `google.maps` but I can't access any of `google.maps.Map` or `google.maps.LatLng`, it's like the api has not been loaded or something... I only could make it working with the global callback function... – MikO May 09 '13 at 18:26
  • Thanks again, but that's exactly the way I've been proceeding so far :). Well, firstly the function `createRenderer` in the place you put it doesn't work, because it's inside and object, right? so I put it outside `util.extend(MyController.prototype, {...})`, and in fact it works, as I said in my question, but from `createRenderer` I can't acces `myVariable`... (`this` inside this function is `Window`)... so we have been walking around but we're in the same point ;) – MikO May 09 '13 at 18:59
  • See my explanation added to the end of the post. – Ryan Lynch May 09 '13 at 19:23
  • You could also consider using Miller Medeiros's async plugin to load google maps as a dependency to your module. https://github.com/millermedeiros/requirejs-plugins – Ryan Lynch May 09 '13 at 19:27
  • Thanks, thanks and a thousand times thanks! after hours and hours of struggling it's working now! Btw, there are a couple of syntax errors in your code that made me confused... And one thing I don't understand is that I've tried and it only works if call the `render(...)` function inside `showMap`, just before the `$.getScript(...)` call... Anyway, scopes and execution order in Javascript is still an obscure world for me... but never mind, it's working ;) Thanks again for taking your time to help me. As I can upvote this answer only once, I'll upvote a couple of answers of yours as a reward ;) – MikO May 09 '13 at 22:25
  • I saw one sytax error, in the property assignment for creatRenderer (= instead of :) any more? – Ryan Lynch May 09 '13 at 23:18
  • Exactly, that was the important one. Then I thought there were some wrong `;` but I can't see it now, so I guess in fact there wasn't any more... thanks again! – MikO May 09 '13 at 23:28