At work we decided to start using it to develop a BIG project and its all made with ExtJS and CodeIgniter for the server side code.
The main requirement is to make the application as fast and reliable as possible and even though we are no even at the middle of it, we already have like 25+ JS files containing a variety of extended components. We also need to use Googlemaps/map24/M$maps depending on the user configuration and so on.
We've tried compressing all of the source code and putting into one single js file, but the file size is about 500kb + Maps API + ExtJS Api + anything else that we will need later.
So, what to do now? Lazy Load to the rescue =)
For all of you who don't know what that means, is "Loading js files on demand" duh!.
I started looking everywhere and found many different options but any of those satisfied my needs.
Requirements:
- Avoid at all cost storing JS code in the database.
- Eval Function is Evil!
- Need to know when the js code has been loaded.
So, I grab the best from all of the approaches reviewed and this is the result:
Ext.ux.lazyLoad is a Singleton, you can copy and paste this code into a js file and start using it as follows:
Ext.ux.lazyLoad.get('gmap', function(obj){
Alert("Google Maps Has Been Loaded");
//Your code goes here and will be executed after loading the js file
},this);
This means that every time we need to call a function that is tied to a class that has been dynamically loaded, we need to do it by proxy-ing using the get function from the lazyLoad object. This object keeps track of what js files has been loaded and if it is loaded already it only calls the callback function supplied by you using the scope that you send.
The lazyLoad Singleton
The function inside the singleton uses the defer function from extjs to check every 2 seconds if the js has been loaded by evaluating the "checkLoad" variable from the "objects" variable. In the mean time, you can put a mask on your components while waiting for the code to load, since this will only happend the first time.
This way, we are sure that the classes, functions and everything we need have been loaded before using em'.
This component is working like a charm on our system, so I hope it can be useful for anyone of my fellow ExtJS developers.
Any questions, critics or suggestions are very welcome =)
Happy Coding , Jerry
/**
* Ext.ux.lazyLoad
*
* @author Jerry Sosa (sosamv) Published with the permission of Near It Services (nearitservices.com)
* @version Ext.ux.lazyLoad.js
* @date 10. February 2001
*
* @license Ext.ux.lazyLoad is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/
Ext.namespace('Ext.ux.lazyLoad');
/********************************************************************
*@name Ext.ux {Singleton}
* Load javascript files onDemand
**/
Ext.ux.lazyLoad = function() {
var debug = false;
var objects = {
gmap:{
loaded:false
,checkLoad : 'GMap2' /*Variable that must exists to confirm load*/
,url:'http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAZtXebGuEUyPUsb16TunzchQH1vzzQPdXCsLMkqVnPRtWvfSdoRToJrguncXU9Z0FgvbgReXeej9BDQ&sensor=false&async=2'
}
,ctrlPropertiesGrid:{
loaded:false
,checkLoad : 'Ext.ux.customControl'
,url:'/app/properties/Ext.ux.customControl.js'
}
//.....ADD HERE MORE FILES
}
return{
/*
* @name Ext.ux.lazyLoad.get
* Retrieve js files on demand if not loaded
* @params
* -jsKey {String}: Js File name to load
* -callback {function}: function to be executed after
* -scope {Object}: Object scope to run the callback function
**/
get : function(jsKey, callback, scope){
/*Return if is not a valid object*/
if(!objects[jsKey]) return;
/*Run thread*/
var waitCounter = 0;
this.thread(jsKey, callback, scope, waitCounter, 'loadScript' + Ext.id());
}
/*
*@name Ext.ux.lazyLoad.thread
*Reusable thread to check if js file has been loaded
*@params
* -jsKey{String}: js key from the objects array
* -callback{Function}: function to execute when file is loaded
* -waitCointer{Integer}: Current cycle number
* -id{String}: Id used to identify the script tag
**/
,thread : function(jsKey, callback, scope, waitCounter, id){
/*If its loaded*/
if(this.isLoaded(jsKey)){
callback.call(scope);
}else{
/*If script object hasnt been included, do it*/
if(!Ext.get(id)){
/*If hasnt been loaded*/
/*Prepare url*/
var url = objects[jsKey].url;
this.head = document.getElementsByTagName('head').item(0);
script = document.createElement('script');
script.src = url;
script.type = 'text/javascript';
script.id = id;
this.head.appendChild(script);
}
if(debug)Ext.get('testdiv').insertHtml('beforeEnd',jsKey + ' wait counter:'+ waitCounter+'
');
try{
var checkVar = eval(objects[jsKey].checkLoad);
objects[jsKey].loaded = true;
callback.call(scope);
this.clean(id);
return;
}catch(e){
if(waitCounter++ <10){
/*Will check 10 times every 2 seconds if the js has been loaded*/
this.thread.defer(2000, this, [jsKey, callback, scope, waitCounter,id]);
}else{
this.clean(id);
alert("Problem loading libraries");
}
}
}
}
/*
* @name Ext.ux.lazyLoad.clean
* Removes recently used script tag from dom
**/
,clean : function(id){
var scriptTag = document.getElementById(id);
if(scriptTag) this.head.removeChild(scriptTag);
}
/*
* @name Ext.ux.lazyLoad.isLoaded
* Getter function to retrieve status
**/
,isLoaded : function(jsKey){
return objects[jsKey].loaded;
}
}
}();
/*USAGE EXAMPLE*/
Ext.onReady(function() {
Ext.ux.lazyLoad.get('gmap', function(obj){
Alert("Google Maps Has Been Loaded");
//Your code goes here and will be executed after loading the js file
},this);
});