Clean Up Your JavaScript with ztemplates and jQuery
Join the DZone community and get the full member experience.
Join For FreeWhen writing webapps that make heavy use of javascript it gets hard to keep track of the javascript used, especially when the webapp loads content dynamically like in AJAX applications. The problem is that every container page has to be aware of the javascript needed by all the dynamcally loaded component markup.
The problems are:
- Which javascript must be loaded
- How to prevent duplicate variable assignments
- dependencies between javascript libraries
In such cases a framework for dynamic javascript loading comes handy, because it relieves the container from the need to know what script is needed in the component. The component declares the needed script and is called after the script is available.
This article describes how to write dynamic javascript enabled webapps with the ztemplates java webframework. Beginning with version 2.3.0 ztemplates includes a small javascript library named zscript that makes dynamic JavaScript loading easy. zscript is fully integrated into ztemplates.
See Dynamic Javascript Loading with zscript and
jquery
published on dzone for a short introduction to zscript.
The library uses jquery, so make sure to include your preferred version.
To use add the following lines to your html head section:
<head> <script type="text/javascript" src="jquery-x.x.x.js"></script> <script type="text/javascript" src="${contextPath}/ztemplates/zscript.js"></script> <script type="text/javascript" src="${contextPath}/ztemplates/zscript-definitions.js"></script> </head>
Now for each reusable piece of javascript functionality you want to make
available to your webapp add a annotated class and a template containing
the javascript. The annotation ensures that ztemplates can find the
javascript. ztemplates makes the script available to the zscript library and adds it to zscript-definitions.js
For example to define a javascript variable (keeping a service object) named 'user' create a java class.
@ZRenderer(ZVelocityRenderer.class) //this defines a javascript object called 'user' @ZScriptDefinition("user") public class ZScript_user { }
and a javascript template in ZScript_user.vm at the same location as the class:
if(typeof user=='undefined') { var user = function() { //private area var loggedIn; function isLoggedIn() { return loggedIn; } function setLoggedIn(loggedInParam) { loggedIn = loggedInParam; } //public area contains methods that can be used from outside return { isLoggedIn: function () { return isLoggedIn(); }, setLoggedIn: function (loggedIn) { setLoggedIn(loggedIn); } }; }(); }
This code calls a method and assigns the return value to a variable called user. This happens only if the variable has not already been created. The return value is a collection of functions that are available for calling. The other functions are private.
To use the javascript from your html page write this to your javascript tag:
<script> zscript.requires(['user'], function(){ user.setLoggedIn(true); if(user.isLoggedIn()){ alert('logged in!'); } }); zscript.requires(['mylib'], function(){ mylib.doSomething(); }); </script>
This states that the javascript library 'user' is required in the callback body, so pass the name as first parameter to the requires method. The second parameter is a callback that will be called as soon as the 'user' library is available (which could also be immediately if the library has already been loaded).
If the code contains more than one call to zscript.requires() the order in which the callbacks are called is preserved. In the example the callback for 'user' is always called before the callback for 'mylib'.
You may define other javascripts like this:
zscript.define('mylib', '/js/mylib.js'); //map the name 'mylib' to the url
ztemplates will ensure the javascript is loaded whenever you use it in a zscript.requires(['mylib'], function(){}) call.
Be aware of the cross-domain restrictions placed upon the locations of
your scripts, so best load them from the same server as your html.
Because there is no defined order in which the scripts are loaded at runtime the libraries should not contain logic that references other dynamically loaded libraries:
var user = function() { var loggedIn; //Declare all the libraries used by this library here, will be executed before requires callbacks zscript.requiresInScript(['dialog', 'mylib' ]); //wrong, mylib may not be loaded mylib.doSomething(); function isLoggedIn() { //OK, because dependency has been declared above and call is in function mylib.doSomething(); return loggedIn; } function setLoggedIn(loggedInParam) { //OK, as dependency has been declared above. dialog.show('Something'); loggedIn = loggedInParam; } return { isLoggedIn: function () { return isLoggedIn(); }, setLoggedIn: function (loggedIn) { return setLoggedIn(loggedIn); } }; }();
Note the if(typeof user=='undefined') condition that ensures that the variable is created only once.
Using this pattern you get a clean separation between java, javascript and html and don't have to worry about script tags in your html header markup or duplicate instantiations of variables. All needed javascript is loaded on demand, or if you don't want that you can instruct ztemplates to collect all javascript in zscript-definitions.js
Weblinks:
- http://www.ztemplates.org the annotation based java webframework.
- Dynamic Javascript Loading with zscript and jquery published on dzone for a short introduction to zscript.
Opinions expressed by DZone contributors are their own.
Comments