The problem is asynchrony. When you are going to download HTML via AJAX, the rest of the function continues ...
function someUsage(){ var t = new Test(); t.init(); // The following carries on whilst the request is loading it does not wait t.element.css({some:style}); // This is why I am null t.someMethodExternalCall(); // This is why I am also null }
To get around this, you can use a callback ...
function someUsage(){ var t = new Test(); t.init(function() {
You will need to change the init function and the loadHtml function to call the callback, and not the init method of the caller, the init function ...
this.init = function(callback) { // Using blank Test.html to determine whether the html has been loaded if(Test.html == "") { var me = this; // Call loadHtml with a callback function Text.loadHtml(function() { // I want to keep the this reference to the object and callback argument me.init(callback); }); // It is loaded so continue set up and then trigger the callback } else { this.initElements(); this.someMethodInternalCall(); callback(); } };
There will still be a problem if you created several of these test classes, as each of them will try to get HTML code while the rest will load.
To get around this, you just need to have a flag that is set by the first call. Any subsequent calls are ignored, but callbacks are recorded for the call when the HTML finishes loading ...
Test.loadHtml = function(callback) { // If already loading roll up callbacks if(Test.loading) { // Callback becomes a function that calls the original callback function // and then the new one Test.callback = (function(original) { return function() { original(); callback(); } }) (Test.callback); // First time it has been called set the flag to prevent multiple loads // and add the callback } else { Test.loading = true; Test.callback = callback; // Added to illustrate the AJAX callback functionality ajax("html", function(response) { Test.html = response; Test.callback(); }); } }();
The preferred approach is to enforce the validity of the object during the creation of the instance, which prevents these race conditions. You throw an error if the class cannot be created correctly, this moves the complexity associated with the order of your operations from the class. As you can see below, this is not so pretty, and you should name the boot step yourself (or run something else).
new Test(); // Errors not loaded! // We must perform the load step to use the class Test.load(function() { new Test(); // Works! });
A more elegant solution, especially for large applications, involves controlling access to the class. You cannot access the class without first completing the loading phase, this ensures that the download is always completed before the class is instantiated.