Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mastering external scripts loading order in Meteor (Google Maps)

I tried unsuccessfully to add a google map(externally loaded script) to a meteor app, and I noticed there were two kinds of problems:

  1. If I do the simple thing and add the main API script to my <head></head>, then it gets rendered last.
  2. When this happens, I am obliged to insert any scripts that depend on the API again in my template's <head> - after the main API script. (otherwise scripts complain they don't see the API blabla..)
  3. Then the time for the actually function call comes - and now putting it inside <head> after the rest won't work. You need to use Template.MyTemplate.rendered.

Basically my question is:

  • What's the cleanest way to handle these kinds of things?
  • Is there some other variable/method I can use to make sure my Google main API file is called very first in my HTML?
like image 786
George Katsanos Avatar asked Mar 05 '13 20:03

George Katsanos


2 Answers

I just released a package on atmosphere (https://atmosphere.meteor.com) that might help a bit. It's called session-extras, and it defines a couple functions that I've used to help with integrating external scripts. Code here: https://github.com/belisarius222/meteor-session-extras

The basic idea is to load a script asynchronously, and then in the callback when the script has finished loading, set a Session variable. I use the functions in the session-extras package to try to make this process a bit smoother. I have a few functions that have 3 or 4 different dependencies (scripts and subscriptions), so it was starting to get hairy...

I suppose I should add that you can then conditionally render templates based on whether all the dependencies are there. So if you have a facebook button, for example, with helpers that check the Session variables, you can give it a "disabled" css class and show "loading facebook..." until all the necessary scripts have loaded.

edit 03/14/2013

There is also an entirely different approach that is applicable in many cases: create your own package. This is currently possible with Meteorite (instructions), and the functionality should soon be available in Meteor itself. Some examples of this approach are:

  • jquery-rate-it: https://github.com/dandv/meteor-jquery-rateit
  • meteor-mixpanel: https://github.com/belisarius222/meteor-mixpanel

If you put a js file in a package, it loads before your app code, which is often a good way to include libraries. Another advantage of making a package is that packages can declare dependencies on each other, so if the script in question is, for example, a jQuery plugin, you can specify in the package's package.js file that the package depends on jQuery, and that will ensure the correct load order.

Sometimes it gets a little more interesting (in the Chinese curse sense), since many external services, including mixpanel and filepicker.io, have a 2-part loading process: 1) a JS snippet to be included at the end of the body, and 2) a bigger script loaded from a CDN asynchronously by that snippet. The js snippet generally (but not always!) makes some methods available for use before the bigger script loads, so that you can call its functions without having to set up more logic to determine its load status. Mixpanel does that, although it's important to remember that some of the JS snippets from external services expect you to set the API key at the end of the snippet, guaranteed to be before the bigger script loads; in some cases if the script loads before the API key is set, the library won't function correctly. See the meteor-mixpanel package for an example of an attempt at a workaround.

It's possible to simply download the bigger js file yourself from the CDN and stick it in your application; however, there are good reasons not to do this: 1) the hosted code might change, and unless you check it religiously, your code could get out of date and start to use an old version of the API 2) these libraries have usually been optimized to load the snippet quickly in a way that doesn't increase your page load time dramatically. If you include the bigger JS file in your application, then your server has to serve it, not a CDN, and it will serve it on initial page load.

like image 186
zorlak Avatar answered Nov 03 '22 00:11

zorlak


It sounds like you're loading your Javascript files by linking it with HTML in your template. There's a more Meteor way of doing this:

From the Meteor Docs:

Meteor gathers all JavaScript files in your tree with the exception of the server and public subdirectories for the client. It minifies this bundle and serves it to each new client. You're free to use a single JavaScript file for your entire application, or create a nested tree of separate files, or anything in between.

So with that in mind, rather than link the gmaps.js into head, just download the un-minified version of gmaps and drop it in you application's tree.

Also from the Meteor Docs:

It is best to write your application in such a way that it is insensitive to the order in which files are loaded, for example by using Meteor.startup, or by moving load order sensitive code into Smart Packages, which can explicitly control both the load order of their contents and their load order with respect to other packages. However sometimes load order dependencies in your application are unavoidable. The JavaScript and CSS files in an application are loaded according to these rules:

Files in the lib directory at the root of your application are loaded first. [emphasis added]

And if the sequence is still an issue, drop the js file into client/lib and it will load before all the Javascript you've written.

like image 37
emgee Avatar answered Nov 02 '22 23:11

emgee