JavaScript property initialization

I created the class in JavaScript as follows:

var Test = function(){ this.element = null; this.init = function() { if(Test.html == "") { Test.loadHtml(this); return; } this.initElements(); this.someMethodInternalCall(); }; this.initElements = function() { // append the loaded html to body // etc... this.element = $("some-element-contained-in-loaded-html-apended-to-body"); } this.someMethodInternalCall = function() { this.element.css({some:style}); // works in this place } this.someMethodExternalCall = function() { this.element.css({some:style}); // dosn't work in this place // I mean here this.element is null. WHY? } }; Test.html = ""; Test.loadHtml = function() { // load html content by an ajax request (jQuery $.ajax method) // and put it in Test.html // then recall the Test.init ethod return function(caller) { Test.html = // load html by ajax... etc... caller.init(); }; }(); function someUsage(){ var t = new Test(); t.init(); t.element.css({some:style}); // error: t.element is null WHY? t.someMethodExternalCall(); // error: this.element is null WHY? } 

As you can see, I explain in the code above. Why, when we set properties after they are initialized, does it just affect calls within the network? How can I create a property that I can change its value?

UPDATE:

It seems I should explain my code. The probe is the element property, not the Test.html and Test.loadHtml or its call. Test.loadHtml starts up correctly (you can check it), and Test.html gets the loaded html and the loaded html get is added to the body and so on. This is a JavaScript template (I forgot its name) and it works correctly. The only thing that’s wrong is property initialization - element .

+7
source share
2 answers

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() { // I do not continue until the loadHtml request has completed t.element.css({some:style}); // I am not null anymore t.someMethodExternalCall(); // I am not null anymore }); } 

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.

 // views is some object managing the loading of view classes when asked for // one or more views it will load the required HTML and return the class(es) // so we can create instances... views.load("Test", function(Test) { var t = new Test(); t.element.css({some: style}); t.someMethodExternalCall(); }); 
+7
source

Do you caller.init(); in ajax function loadHtml in loadHtml ?

If not, your init function will be added to the execution stack before your html is loaded (and why this.element is null)

 Test.loadHtml = function() { // load html content by an ajax request (jQuery $.ajax method) // and put it in Test.html // then recall the Test.init ethod return function(caller) { $.ajax({ url: 'somethinghere', success: function(data) { Test.html = data; caller.init(); } }); }; 
+2
source

All Articles