Skip navigation

Tag Archives: dojo

SUMMARY:

Using goog.require and goog.provide for your own JavaScript packages and modules will not automatically work. You have to use goog.addDependency, and this is best done using a provided python script which will help you create a dependency file.

Update:
Added a HOWTO for using Google Closure’s Template files with your own code.

The difference between dojo.require and goog.require
(skip if you don’t care about Dojo)

Coming from the world of Dojo to the Google Closure Library I thought I could use goog.require and goog.provide the way I am used to with Dojo. Nope, it’s done differently.

The big difference obviously is that with Dojo, dojo.require is magic and happens automatically. Dojo will use an XMLHttpRequest to evaluate your JavaScript on the fly. Google’s system works differently, it creates script tags. Google’s system is better for debugging. Dojo provides some options for also using script tags but because that’s not the intended means of using dojo.require it can lead to problems.

Using script tags makes debugging easier because in debugging tools (such as Firebug, IE’s Developer Tools, or Webkit’s Web Inspector) JavaScript provided with script tags can be located by filename. How evaluated code shows up in debugging tools depends on the tool but it’s never simple or easy and becomes worse the more JavaScript you’ve evaluated.

Another difference is that goog.require doesn’t request your code if it doesn’t already know where to find it. You tell Closure where to find it by using goog.addDependency, an extra step not required in Dojo. The best way to do this is to use a python script included with the framework.

With Dojo you could dump your dojo.require wherever you wanted, similar to use of #include in C source files. You don’t want to do this with the Google Closure Library as the code is added via a script tag and wont be available until that script tag has been added. Instead create a single place where all your goog.require statements are, probably a separate JavaScript file or in a separate script tag in your markup.

There’s also a difference between Dojo’s custom builds and the Google Closure Compiler. Google Closure Compiler is a separate tool but the best means to use this tool if you’re already using the Google Closure Library is to use the provided python script.

Having explained the differences there are also similarities: directory structure is just as important when using goog.require as it is with dojo.require. If your goog.provide is “myapp.subapp.view” you should have a directory structure as you would for dojo (more below).

The three options

Before I explain the means by which you can use goog.require and goog.provide for your own code you need to know about the three options available when you use the calcdeps.py python script provided by the framework:

  • deps – generate a dependance file – If you use this option, when your site is loaded in the browser many script tags are added, one for each file.
  • script – generates a single JavaScript file that includes all of your code and parts of the goog namespace you’re using. You will only have one script tag, but your JavaScript will be otherwise unchanged.
  • compiled – generates a single compiled JavaScript file that includes everything you need. Requires Google Closure Compiler’s compile.jar (available separately). You will only have one script tag of minified JavaScript. Your code will be significantly modified, the degree to which depend on the compiler flags provided.

Most of the details of these three main options (there’s also a list option useful for information purposes only) can be read about on the documentation page for the calcdeps.py script. What the docs are not clear about is how to point the script to your code. I will explain this to save you a period of trial and error and also show an example.

Initial setup

In this set up I keep goog and my Javascript separate. I don’t like including big external libraries in my source control and Google Closure Library is over 100MB! I used to include JavaScript libraries but as they’ve grown in size and complexity I’ve decided to stop doing this, similar to external python libraries. This is handy if you like to create a lot of releases and branches and don’t want to take up hundreds of megabytes of hard drive space on your subversion server for every branch and release.

My directory structure is as such where / is web root (not / on the computer):

/index.html
/media/css/
/media/js/goog/
/media/js/myapp/
/meda/js/release/

/media/js/goog/ is a symlink to what in the Google Closure docs would be described as closure-library-read-only/closure/goog/. This is on my dev machine where I have the Google Closure library. I don’t have the Google Closure Library on the production machine because when I create a build I don’t need it.

/media/js/myapp/ are the checked in files for my JavaScript app. It is the root for my application.

/meda/js/release/ is where my compiled JavaScript files are, I’ll only use this in production.

Let’s say I have a sub application with two files:

/media/js/myapp/subapp/controller.js
/media/js/myapp/subapp/view.js

At the top of the file in in /media/js/myapp/subapp/controller.js I will put:

goog.provide( "myapp.subapp.controller" ); 

and in the other file I’ll put:

goog.provide( "myapp.subapp.view" ); 

Whatever code these files have go below these lines.

Next I create a file that will have all my goog.require statements:

/media/js/myapp/require.js

The contents of this file will be:

goog.require( "goog.events" );
goog.require( "goog.dom" );
goog.require( "myapp.subapp.controller" );
goog.require( "myapp.subapp.view" );

I have added require statements for goog.dom and goog.events because I will be using those parts of Google Closure Library in my own code.

What is in my index.html file varies depending on if I am using it for development or production. In development I’ll have:

<script type="text/javascript" src="/media/js/goog/base.js"></script>
<script type="text/javascript" src="/media/js/myapp/deps.js"></script>
<script type="text/javascript" src="/media/js/myapp/require.js"></script>

Note the deps.js, this is a generated file. In production I’ll simply have:

<script type="text/javascript" src="/media/js/release/subapp-compiled.js"></script>

I create different files for every subapp because each subapp in my architecture exists on a separate page. This might be different for you. This would require a different require.js for every subapp, and during compilation additional compilations for every compiled file.

Using calcdeps.py to create a dependency file

This section explains how to use the calcdeps.py script included in the Google Closure Library. I recommend reading the documentation carefully. Even so, what I describe below, with regard to the difference between creating a dependency file and a compiled file and the paths is not included in the documentation.

IMPORTANT: On your development machine find your way to your /media/js/myapp directory. You will want to be here when you call the calcdeps.py script. If you are in any other directory this will fail because of how the script works. There is a CLOSURE_BASE_PATH variable you can set in your JavaScript that can help with any errors you might run into if you do this differently.

I call the calcdeps.py as such (closure-library-read-only is the path to Google Closure Library on your system):

closure-library-read-only/closure/bin/calcdeps.py -i ./require.js -p .. -o deps > deps.js

This created:

// This file was autogenerated by calcdeps.py
goog.addDependency('../utils.js', [], []);
goog.addDependency('../myapp/deps.js', [], []);
goog.addDependency('../myapp/require.js', [], ['goog.events', 'goog.dom', 'myapp subapp.controller', 'myapp subapp.view']);
goog.addDependency('../myapp/subapp/controller.js', ['myapp.subapp.controller'], []);
goog.addDependency('../myapp/subapp/model.js', ['myapp.subapp.Model'], []);
goog.addDependency('../myapp/subapp/view.js', ['myapp.subapp.view'], []);

Couple of things of note:

Notice utils.js and model.js. I didn’t talk about these files or add them to require.js because I’m not using them but look the script found them anyway. calcdeps.py uses the path given via -p option and traveres all its subdirectories recursively looking for files. It searches the files for goog.require and goog.provide statements and uses those to calculate what goog.addDependency statements it needs to create.

The first argument to goog.addDepedency is the file name, the second is the goog.provide statements found in the file, and the third is the goog.require statements in the file.

The .. path is relative to /media/js/goog/base.js. Very important.

If you now request index.html and look in Firebug’s net tab you’ll see requests for all of the necessary dependencies for goog.events and goog.dom, as well as

/media/js/myapp/deps.js
/media/js/myapp/require.js
/media/js/myapp/subapp/controller.js
/media/js/myapp/subapp/view.js

You wont see:

/media/js/myapp/subapp/model.js
/media/js/utils.js

because you haven’t requested them with goog.require. You can simply add another goog.require statement for /media/js/myapp/subapp/model.js to /media/js/myapp/require.js without calling calcdeps.py again and it will request the file! utils.js doesn’t provide a goog.provide statement so you have to create a separate script tag for it.

Using calcdeps.py to create a compiled file

Remember: You need to download compile.jar separately, it’s not included in the library!

For the purpose of creating a dependency file we used relative paths which created relative paths in your deps.js file. For creating a compiled file we will use absolute paths and we have to add the path to the Google Closure Library in addition to the path to our own code.

I changed my current working directory to /media/js/release and call this in my terminal:

closure-library-read-only/closure/bin/calcdeps.py -i /Users/Bjorn/projects/site/media/js/myapp/require.js -p closure-library-read-only/ -p /Users/Bjorn/projects/site/media/js/myapp/ -c /Users/Bjorn/closure/compiler/compiler.jar -o compiled > subapp-compiled.js

/Users/Bjorn/projects/site is where my website is in my file system.
closure-library-read-only is wherever your copy of the ENTIRE Google Closure Library is on your system, not just the JavaScript.
/Users/Bjorn/closure/compiler/compiler.jar is where I have put the compiler.jar file for the purpose of this example on my file system.

Running this statement creates:

/media/js/release/subapp-compiled.js

This is a JavaScript files that has all the code I need to run subapp. If I later want to include myapp.subapp.model I have to rerun the statement. That’s why it’s better to use a dependency script for dev and compiled file for production. You don’t want to have to compile your code every time you edit your files during development and debugging obfuscated code created by compilation wont be fun. In production however you don’t need to do any of this and having one file reduces page load times for users.

In conclusion
I hope this has helped you figure out how to use goog.require and goog.provide for your own JavaScript code. I recommend also looking at the compilation flag options and reading the rest of the documentation.

Dojo’s stopEvent method is a crossbrowser compatible way to stop further event bubbling when you want to take charge and control what happens manually. For example if you add an onclick event to an anchor and don’t want the browser to go the page specified in the elements href attribute, you can use the stopEvent method to prevent it from happening. You can also prevent also other many things, including button submits and the checking or unchecking of checkboxes. However, there’s a bug here! If you do use stopEvent to stop a checkbox toggle, you can’t set it manually either! Copy and paste this code and save it in an .html file and then open it up in a browser to see for yourself (the script tag points to a version of dojo hosted by Google):

<html>
<head>
<SCRIPT TYPE="text/javascript" SRC="http://ajax.googleapis.com/ajax/libs/dojo/1.2/dojo/dojo.xd.js"></SCRIPT>
<title>Script checkbox test thingy</title>
</head>
<body>
<h1>Check this box</h1>
<div id="container">
<input type="checkbox" id="cb" />
<label for="cb">Check it</label>
</div>
<script type="text/javascript">
dojo.connect(dojo.byId("container"),"onclick",function(e){
dojo.stopEvent(e);
dojo.byId("cb").checked = true;
});
</script>
</body>
</html>

There is a solution however! Use setTimeout with a wait time of 0 to attempt to check the box, and it will work!

window.setTimeout(function(){ dojo.byId("cb").checked = true;},0);