I am trying to use the Javascript Module pattern. I want my module to have a public method that can be called when a json file is loaded. In this simple example, the module loads the json on init and (should) load an image into a DOM element when the public method 'loadPic' is called. The source of the image is in the json file.) When I run the script the first time, I get the following error: Uncaught TypeError: Cannot read property 'src' of undefined. My interpretation is that the method 'loadPic' is called automatically, before the data is loaded... but I don't know how to prevent that. Here is the code (PS: I am using Zepto, the 'light' version of jQuery):
<!-- language: lang-js -->
/* in module file: DualG.js */
;(function ($) {
$.extend($.fn, {
DualG: function (element, options) {
var defaults = { // not used yet...
indexStart:0,
url: 'js/data.json'
},
my = {},
init,
loadPic,
plugin = this;
plugin.settings = {};
plugin.pics = [];
init = function () {
plugin.settings = $.extend{}, defaults, options);
$.getJSON(plugin.settings.url, function (data) {
var l = data.images.length, i;
for (i = 0; i < l; i = i + 1) {
plugin.pics[data.images[i].index] = data.images[i];
}
});
init();
my.loadPic = function (index, container) {
$(container).empty().append($("<img>").attr({"src":plugin.pics[index].src}));
};
return my;
}
});
}(Zepto));
My problem was that data is loaded a-synchronistically - so I was tying to use data that was not loaded yet. I added an event trigger in the init, after loading the data. Then, I listen to the event and call the loadPic method only then... Thanks all for your help.
Related
I have an external (on a server) JS file I am calling and need to read.
This is for Captcha use.
I am having an issue since the TlvJs object is undefined while calling the JS file.
When debugging, I have noticed that the $(document).ready is being executed before the TlvJS was actually declared.
When using the files locally (keeping them in assets folder), first, the TlvJS is being declared and just then the $(document).ready.
How can I make the TlvJS object declared first, even when the file is calling externally?
Please note, I can't control the order of the objects inside this JS file.
$(document).ready(function () {
//select all controls
var elements = document.querySelectorAll('div[tjs-control]');
//iterate thru all found controls
for (var i = 0; i < elements.length; i++) {
var elm = elements[i];
switch ($(elm).attr('js-control')) {
//Captcha control
case "TlvJs.UI.Captcha":
var cap = TlvJs.UI.Captcha($(elm).attr('id'));
var options = $(elm).attr('js-options');
setInstanceProps(options, cap);
cap.url = SetClientProtocol(cap.url);
cap.initCaptcha();
$.prototype.Captcha = cap;
break;
}
}
});
var TlvJs = {
UI: {
Captcha: function (tagId) {
var cap;
cap = Captcha(tagId);
return cap;
},
FileUploader: function (options) {
return FileUploader(options);
}
},
Utils: {
},
Navigation: {
}
};
Currently, I am adding the JS files manually inside a .ts file since when adding the Js files inside the index.html the elm element in the JS file is undefined. That way, the Html page wasn't actually loaded yet.
The first way I have tried (with index.html) which cause the "unknown" elm element:
<script src="http://xxx/Services/CaptchaServicesDistributed/Scripts/TlvJs-Captcha.js"></script>
<script src="http://xxx/Services/CaptchaServicesDistributed/Scripts/TlvBaseJs.js"></script>
The second (and current) way is :
let tlvBaseScript = document.createElement("script");
tlvBaseScript.defer = true;
tlvBaseScript.src = this._configService.getTlvBaseScriptUrl();
tlvBaseScript.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(tlvBaseScript);
I would like to use WebAssembly within a web worker.
From my main application, I launch it like this:
let w = new Worker('test.js');
w.onmessage = (event) => { console.log(event); };
w.onerror = (event) => { console.error(event); };
w.postMessage({ message: "Hello World" });
Then, I created a file test.js as follows:
self.Module = {
locateFile: function (s) {
console.log(s);
return s;
}
};
self.importScripts("main.js");
// note: `main.js` is the JavaScript glue file created by emcc
self.onmessage = function(messageEvent) {
console.log(messageEvent); // works!
console.log(self.Module); // works!
console.log(self.Module.ccall("test")); // crashes!
}
I get an error: Uncaught TypeError: Cannot read property 'apply' of undefined. I don't understand why self.Module is undefined, how is that possible?
I have the feeling there is something about the scope of the web worker and WebAssembly that does not work well together.
Thanks for your input!
The problem is that console.log() does not reveal the true state of the object at execution time. Further digging revealed that in fact the object Module was not ready yet.
I cite from: https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html
How can I tell when the page is fully loaded and it is safe to call compiled functions?
Calling a compiled function before a page has fully loaded can result
in an error, if the function relies on files that may not be present
[...]
Another option is to define an
onRuntimeInitialized function:
Module['onRuntimeInitialized'] = function() { ... };
That method will be called when the runtime is ready and it is ok for you to call compiled code.
Adjusting my test.js (worker) file fixes the issue:
self.Module = {
locateFile: function (s) {
console.log(s);
return s;
}
// Add this function
onRuntimeInitialized: function() {
test();
}
};
self.importScripts("main.js");
// note: `main.js` is the JavaScript glue file created by emcc
self.data = {};
// to pass data from the main JS file
self.onmessage = function(messageEvent) {
console.log(messageEvent); // works!
self.data = messageEvent; // save the data
}
// gets executed when everything is ready.
self.test = function() {
// we may safely use self.data and self.Module now!
console.log(self.Module.ccall("test")); // works!
}
I'm working on a Flask app where some data gets passed to a template, and I want to make that data available to multiple instances of an object. Here's what it would look like if I just hardcoded the desired data into my .js file:
var Module = function(selector) {
var targetDiv = selector,
targetData = 'lorem ipsum sit dolor',
show = function() {
$('<p>' + targetData + '</p>').appendTo(targetDiv);
};
return {
show: show,
};
};
$(document).ready(function() {
firstModule = new Module($('#first'));
secondModule = new Module($('#second'));
firstModule.show();
secondModule.show();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='first'></div>
<div id='second'></div>
I can't just call a function on an unconstructed Module object, so my next step is to create a static-like ModuleFactory that I can load with data from jinja, and then create Modules from there:
var ModuleFactory = function() {
var targetData = null,
setData = function(data) {
targetData = data;
},
create = function(selector) {
return new Module(selector, data);
};
return {
setData: setData,
create: create,
};
} ();
Then I attempt to call ModuleFactory.setData({{data}}); from a <script> tag in the HTML, and do something like ModuleFactory.create($('#first')).show(); in the .js
But of course because I have to include my .js file before using the ModuleFactory in the HTML, I end up constructing the objects before the factory is initialized.
(Past this point, my workaround attempts stop being relevant to the problem.)
Anyway, what's the correct way of getting data from Jinja to my JS module? There has to be a common pattern or something.
This feels awful, but it works:
Have an object with a runProgram method, which takes the desired data as a parameter, then executes the logic that had previously been inside the document ready function. e.g.
var ProgramRunner = function() {
var runProgram = function(data) {
ModuleFactory.setData(data);
firstModule = ModuleFactory.create($('#first'));
firstModule.show();
};
return {
runProgram: runProgram;
};
};
Then just
<script>
$(document).ready(function() { ProgramRunner.runProgram({{data}}) });
</script>
in the HTML.
(Leaving question open, because I suspect there's a much better way of handling this.)
I have a few questions about Best Practises using javascript in external files and namespacing.
Let's have a namespace MyCompany, global configuration stuff, code for individual pages and maybe some "API"s.
var MyCompany = {};
Global configuration in HTML
MyCompany.root = "/";
Which approach is better
First
MyCompany.Page = {};
(function(ns} {
ns.init = function() {
var root = MyCompany.root;
ajax(root+"somepage.html");
};
}(MyCompany.Page.Home = MyCompany.Page.Home || {});
and in html use
<script>
$( function() {
MyCompany.Page.Home.init();
});
</script>
Second (Page as an Class and its instance)
MyCompany.Page.Home = function() {
var root = MyCompany.root;
this.init = function() {
ajax(root + "somepage.html");
};
};
in html
<script>
var page = new MyCompany.Page.Home();
$( function() {
page.init();
});
</script>
Submodules and Mixing API with Page javascript
If our Homepage has some reviews.
MyCompany.Page.Home.Reviews = function() {
this.init = function() {
load_stuff();
}
};
And now inside Page init use
MyCompany.Home.Page = function(data) {
var reviews = new MyCompany.Home.Page.Reviews();
this.init = function() {
reviews.init();
};
};
Could that cause troubles?
It's obvious that Reviews extends MyCompany.Home.Page, but MyCompany.Home.Page requires Reviews.
It shouldn't cause troubles if instance on MyCompany.Home.Page is created after MyCompany.Home.Page.Reviews are loaded, right? Because Reviews in fact will extend the function object, is that right?
I guess this depends on answer to first question.
It also could be
(function(ns) {
ns.init = function() { MyCompany.Page.Home.Reviews.init(); };
})(MyCompany.Page.Home = MyCompany.Page.Home || {} );
(function(ns) {
ns.init = function() { load_stuff(); };
})(MyCompany.Page.Home.Reviews = MyCompany.Page.Home.Reviews || {});
Also should I somehow separate API of Page javascript?
Such as
MyCompany.APIS.Maps = function(location) {
/* Private variables */
var _location = location;
/* Private functions */
function search_address(address) { .. do search .. }
/* Public interface */
this.search = search_address;
do some initialization ...
};
I'd be glad if anyone reads it all to leave some comment.
Thank you in advance.
Which approach is better? Revealing singleton module (first) or a constructor function/class and its instance (second)?
Depends on your use case. If you don't expect multiple page objects to exist at once (and you hardly seem to), the singleton (with an init function) is really fine. Everything else could be considered wrong or at least overkill.
Same thing holds true for your MyCompany.Page.Home.Reviews (or MyCompany.Home.Page.Reviews?) class module, of which you seem to need only one instance.
It shouldn't cause troubles if instance on MyCompany.Home.Page is created after MyCompany.Home.Page.Reviews are loaded, right? Because Reviews in fact will extend the function object, is that right?
Yes.
(function(ns) {
ns.init = function() { MyCompany.Page.Home.Reviews.init(); };
})(MyCompany.Page.Home = MyCompany.Page.Home || {} );
If you have that ns shortcut available, you should use it:
(function(ns) {
ns.init = function() { ns.Reviews.init(); };
})(MyCompany.Page.Home = MyCompany.Page.Home || {} );
Also should I somehow separate API of Page javascript?
For development: Yes, in every case. Each module should have its own file. When deploying, you might concatenate them together for faster loading, but that's a different question.
ok so i built a dynamic content swapping system using mootools for my website and I'm having trouble with one aspect about it. When it pulls the new content off the server it also gets a snippet of code which is to be executed by the Type Function
setContent: function(content) {
var self = this;
window.history.pushState({x: 0}, "x", content[0].toLowerCase());
var mainField = $('meat');
mainField.set('html', content[1]);
if(content[2] != false) {
var functionn = Function(content[2]);
functionn();
}
},
Now in this situation im trying to execute a method in the class:
addLink: function(item) {
var self = this;
var object = $(item);
self.menus.include(item);
object.addEvent('click', function(event) {
event.stop();
});
},
by sending this to the set content method to be executed:
self.addLink('#order');
now when I try it out i get this error:
Uncaught TypeError: Object [object Window] has no method 'addLink'
I guess self refers to window rather than to your class, see the example at http://mootools.net/docs/core/Types/Function#Function:bind. Use bind() to bind this to your class and then use this in your server reply.
if(content[2] != false) {
var functionn = Function(content[2]);
var functionn_bound = functionn.bind(this);
functionn_bound();
}