How to invoke a method when a module is loaded? - javascript

I'd like to compose a demo showing the order of loading of modules. Supposed there are two modules Uno and Duo and I'd like to print their names to the console as they're loaded. How can I do that? I'm talking about the following pseudo-code.
Uno.js
import Duo from "./duo.js";
default export {
loaded: () => { console.log("Uno loaded"); }
}
Duo.js
default export {
loaded: () => { console.log("Duo loaded"); }
}
I've tried a couple of wild shots like loaded, init, start etc. but didn't get any print outs. Then, I goolearched it for a while but didn't see any examples showing anything related except for e.g. Vue framework where the event seems to be build in. Adding the term construct produces a wave of comparisons between modules and prototypes.
Usually, when there's no tons of examples on how to, it implies that it's not possible to achieve. However, since I haven't found any statements contradicting that it's doable, I'm still a bit hopeful.
I've seen the approach chaining the requireand subsequent statements but in my case, I want specifically the module itself to be self-containedly verbose.

You are currently exporting a function that does what you want from the module. Which means someone would need to invoke that function, and no, there's no automatic hook that invokes an exported item with a certain name when a module is loaded.
Instead, simply call the function yourself right in the module body. When the module is being evaluated, you are free to perform any side effects that you want:
// uno.js
import "./duo.js";
console.log("Uno loaded");
// duo.js
console.log("Duo loaded");

Related

How to export functions for unit tests in PortalScript (ECMAScript5)

We have an application server supporting an embedded server side scripting language called PortalScript (SpiderMonkey V1.8.5), which is compliant to the ECMA-Script-262 Edition 5 (ECMAScript5) specification. The Portalserver supports numerous so-called scripting hooks and event hooks, which allow to embed our scripts. These attached scripts implement our business logic which I want to test e.g. with Jasmine, Jest, etc.
The first problem is that return statements at the end of the scripts are mandatory, and these return statements are not within functions. A minimal example could look like this:
// #import "myFctLib"
var currentModel = given_context.model;
if (!currentModel) {
return -1;
}
currentModel.setAttribute("$fieldId", "SUCCESS");
return 0;
These scripts can be quite large. So if we extract functions (within a script) to just test them, how could we call these functions from the tests? How can we export functions compliant to PortalScript? Does someone know if it's possible to use the CommonJS module system with PortalScript?
Currently we don't use a module system but a precompiler to replace code for statements like // #import "myFctLib" (see first line in the example code) in the scripts to use functions from other scripts...
In such a context, how would you write (unit) tests? How would your approach look like?
I assume PortalScript will eventually wrap the embedded script in its own function and call that. I'd also assume that for testing purposes, you don't want to execute the entire embedded script function. These two conditions seem mutually exclusive.
Would it be an option to separate the logic into two files, one defining the business logic in small, testable functions, and one file including the former and then just calling main()
myFctLib.js:
// #import "someOtherFctLib"
function setSuccessAttribute(model) {
if (!model) {
return -1;
}
model.setAttribute("$fieldId", "SUCCESS");
return 0;
}
myPortalScriptFile.js
// #import "myFctLib"
return setSuccessAttribute(given_context.model);
You'd then only test myFctLib.js, which still leaves room for errors in myPortalScriptFile.js, but that room should be fairly small.

react external javascript integration

I'm tring to merge 2 projects i found:
https://github.com/danxfisher/MeetEasier
and
this page https://tympanus.net/Development/Interactive3DMallMap/
i've done the changes in the react project meeteasier and included the html parts from interactivemallmap(with my little react abd nodejs knowledge) . I have problems on calling the javascript from main.js(interactivemallmap).
Here is the problem:
The first time i load the page with the chromedev i see that in the main.js it gets the values but when i recall the functions that values are null:
original main.js start like this and dot work:
;(function(window) {
...
mall = document.querySelector('.mall'),
...
pins = [].slice.call(mallLevelsEl.querySelectorAll('.map__space')),
....
pins.forEach(function(pin) {
var contentItem = contentEl.querySelector('.content__item[data- space="' + pin.getAttribute('data-space') + '"]');
pin.addEventListener('mouseenter', function() {
//if( !isOpenContentArea ) {
classie.add(contentItem, 'content__item--hover');
//}
});
....
})(window);
i've tried with the $(document).ready(function(){ instead of ;(function(window) {
but it is not working or better: in the first page load the it gets its value, but after when i pass over with the mouse and the event mouseenter triggers all the values are undefined
I think this is not the right approach but how do i make it work?
I do not have enough code to fully understand what is going on, but seeing that you're trying to merge two big projects with different codebases, if I were you I should take it slow, and take some steps back.
Your problem:
The main problem is that, to use React, you should first have a fundemental understanding of what it is doing under the hood.
You're trying to implement jQuery (a DOM library) with React, however, this will not work this easily, because React works with something that's called a virtual DOM, and it does not have access to the regular DOM nodes unless specifically ordered to do so (using findDOMNode from the react-dom package handles this for example).
What you should do:
Please read the docs first:
https://reactjs.org/docs/hello-world.html
Then, after you understand React, try implementing the SVG maps in it, step by step.
Just a small insight:
Rendering SVG in your render method, or try rebuilding it with D3 (https://d3js.org/) for example, just a suggestion on how to handle complex SVG.
You should handle most logic that's from main.js in the componentDidMount lifecycle, assuming you're going to need to access the map nodes.

how should I write my define to work with curl.js?

I'm reading Addy Osmani's excellent blog post about writing AMD modules. I start with a simple chunk of js that I lifted from his post:
define('modTest', [],
// module definition function
function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
I took out the dependencies on foo and bar. Just want a simple object that logs to the console.
So I save that in /js/modTest.js and then try to load it:
curl(['/js/modTest.js'])
.then(function(mt) {
console.log("Load complete");
console.log("mt:");
console.log(mt);
mt.doStuff()
}, function(ex) {alert(ex.message);})
Result: error: Multiple anonymous defines in URL. OK that didn't work. Tried adding in a namespace: define('myCompany/modTest', [],, same result. Tried adding an empty string in the dependency array, same result.
Also tried curl(['modTest.js'], function(dep){console.log(dep)}); with the same result.
Is the code in Addy's blog post incorrect? Am I doing something wrong? Maybe a bug in curl?
Update 5/24: I ditched curl.js in favor of require.js. Zero odd errors, very little work to change over. I did have to deal with amdefine a bit to get my code running client and server side (one object is in both places, so grunt had to be configured to take care of that). My defines generally look like:
define(->
class AlphaBravo
...
And never have any trouble loading.
You asked curl() to fetch a module called "/js/modTest.js". It found the file and loaded it and found a module named "modTest", so it complained. :) (That error message is horribly wrong, though!)
Here's how you can fix it (pick one):
1) Remove the ID from your define(). The ID is not recommended. It's typically only used by AMD build tools and when declaring modules inside test harnesses.
2) Refer to the module by the ID you gave it in the define(). (Again, the ID is not recommended in most cases.)
curl(['modTest'], doSomething);
3) Map a package (or a path) to the folder with your application's modules. It's not clear to me what that would be from your example since modTest appears to be a stand-alone module. However, if you were to decide to organize your app's files under an "app" package, you packages config might look like this:
packages: [ { name: 'app', location: 'app' } ]
Then, when you have code that relies on the modTest module, you can get to it via an ID of "app/modTest".
curl(['app/modTest'], doSomething);
I hope that helps clear things up!
Fwiw, Addy's example could actually work with the right configuration, but I don't see any configuration in that post (or my eyes missed it). Something like this might work:
packages: [ { name: 'app', location: '.' } ]
-- John
I've just had a similar problem which turned out to be the include order I was using for my other libraries. I'm loading handlebars.js, crossroads.js, jquery and a few other libraries into my project in the traditional way (script tags in head) and found that when I place the curl.js include first, I get this error, but when I include it last, I do not get this error.
My head tag now looks like this:
<script type="text/javascript" src="/js/lib/jquery.js"></script>
<script type="text/javascript" src="/js/lib/signals.js"></script>
<script type="text/javascript" src="/js/lib/crossroads.js"></script>
<script type="text/javascript" src="/js/lib/handlebars.js"></script>
<script type="text/javascript" src="/js/lib/curl.js"></script>
<script type="text/javascript" src="/js/main.js"></script>
You have a problem with your define call. It is NAMED
See AMD spec for full story on how to write defines, but here is what I would expect to see in your js/modTest.js file:
define(/* this is where the difference is */ function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
Now, the boring explanation:
CurlJS is awesome. In fact, after dealing with both, RequireJS and CurlJS, I would say CurlJS is awesome-er than RequireJS in one category - reliability of script execution ordering. So you are on the right track.
On of the major things that are different about CurlJS is that it uses "find at least one anonymous define per loaded module, else it's error" logic. RequireJS uses a timeout, where it effectively ignores cases where nothing was defined in a given file, but blows up on caught loading / parsing errors.
That difference is what is getting you here. CurlJS expects at least one anonymous (as in NOT-named) define per loaded module. It still handles named defines fine, as expected. The second you move the contents of "js/modTest.js" into inline code, you will have to "name" the define. But, that's another story.

Starting with RequireJS, communication between modules

I am an ActionScript 3 developer who is just making his first way in building a large-scale JavaScript app.
So I understand modules and understand that AMD is a good pattern to use. I read about RequireJS and implemented it. However, what I still don't understand is how to achieve Cross-Module communication. I understand that there should be some kind of mediator...
I read articles and posts and still couldn't understand how to implement it simply.
Here is my code, simplified:
main.js
require(["Player", "AssetsManager"], function (player, manager) {
player.loadXML();
});
Player.js
define(function () {
function parseXml(xml)
{
// NOW HERE IS THE PROBLEM -- how do I call AssetsManager from here???
AssetsManager.queueDownload($(xml).find("prop").text());
}
return {
loadXML: function () {
//FUNCTION TO LOAD THE XML HERE, WHEN LOADED CALL parseXml(xml)
}
}
});
AssetsManager.js
define(function () {
var arrDownloadQueue = [];
return {
queueDownload: function(path) {
arrDownloadQueue.push(path);
}
}
});
Any "for dummies" help will be appreciated :)
Thank you.
To load up modules from another modules that you define(), you would simply set the first parameter as an array, with your module names in it. So let's say, in your code, you wanted to load Player.js into AssetsManager.js, you would simply include the string Player in the array.
This is simply possible because define's abstract implementation is equivalent to require, only that the callback passed to define expects a value to be returned, and that it will add a "module" to a list of dependencies that you can load up.
AssetsManager.js
define(['Player'], function (player) {
//... Your code.
});
However, if I can add to it, I personally prefer the use of require inside of the callback passed to define to grab the dependency that you want to load, instead of passing parameter to the callback.
So here's my suggestion:
define(['Player'], function () {
var player = require('Player');
});
And this is because it's much more in tune with CommonJS.
And this is how main.js would look like formatted to be more CommonJS-friendly:
require(["Player", "AssetsManager"], function () {
var player = require('Player');
var manager = require('AssetsManager');
player.loadXML();
});
But the CommonJS way of doing things is just a personal preference. My rationale for it is that the order in which you input the dependency names in the array might change at any time, and i wouldn't want to have to step through both the array and the parameters list.
Another rationale of mine (though, it's just pedantic), is that I come from the world of Node.js, where modules are loaded via require().
But it's up to you.
(This would be a reply to skizeey's answer, but I don't have enough reputation for that)
Another way of solving this problem without pulling in Player's AssetManager dependency via require is to pass the AssetManager instance that main.js already has around. One way of accomplishing this might be to make Player's loadXML function accept an AssetManager parameter that then gets passed to parseXml, which then uses it. Another way might be for Player to have a variable to hold an AssetManager which gets read by parseXml. It could be set directly or a function to store an AssetManager in the variable could be used, called say, setAssetManager. This latter way has an extra consideration though - you then need to handle the case of that variable not being set before calling loadXml. This concept is generally called "dependency injection".
To be clear I'm not advising this over using AMD to load it in. I just wanted to provide you with more options; perhaps this technique may come in handier for you when solving another problem, or may help somebody else. :)

How can I combine my JavaScript files and still have my callbacks wait for a ready state?

I have lots of functions and event handlers that are split across multiple javascript files which are included on different pages throughout my site.
For performance reasons I want to combine all of those files into 1 file that is global across the site.
The problem is I will have event handlers called on elements that won't necessarily exist and same function names.
This is an example of a typical javascript file...
$(document).ready(function(){
$('#blah').keypress(function(e){
if (e.which == 13) {
checkMap();
return false;
}
});
});
function checkMap() {
// code
}
function loadMap() {
// code
}
I would need to seperate this code into an object that is called on that specific page.
My thoughts are I could re-write it like this:
(function($) {
$.homepage = {
checkMap: function(){
// code
},
loadMap: function(){
//code
}
};
})(jQuery);
And then on the page that requires it I could call $.homepage.checkMap() etc.
But then how would I declare event handlers like document.ready without containing it in it's own function?
First of all: Depending on how much code you have, you should consider, if serving all your code in one file is really a good idea. It's okay to save http-requests, but if you load a huge chunk of code, from which you use 5% on a single page, you might be better of by keeping those js files separated (especially in mobile environments!).
Remember, you can let the browser cache those files. Depending on how frequent your code changes, and how much of the source changes, you might want to separate your code into stable core-functionality and additional .js packages for special purposes. This way you might be better off traffic- and maintainance-wise.
Encapsulating your functions into different objects is a good idea to prevent unnecessary function-hoisting and global namespace pollution.
Finally you can prevent calling needless event handlers by either:
Introducing some kind of pagetype which helps you decide calling only the necessary functions.
or
checking for the existence of certain elements like this if( $("specialelement").length > 0 ){ callhandlers}
to speed up your JS, you could use the Google Closure Compiler. It minifies and optimizes your code.
I think that all you need is a namespace for you application. A namespace is a simple JSON object that could look like this:
var myApp = {
homepage : {
showHeader : function(){},
hideHeader : function(){},
animationDelay : 3400,
start : function(){} // the function that start the entire homepage logic
},
about : {
....
}
}
You can split it in more files:
MyApp will contain the myApp = { } object, maybe with some useful utilities like object.create or what have you.
Homepage.js will contain myApp.homepage = { ... } with all the methods of your homepage page.
The list goes on and on with the rest of the pages.
Think of it as packages. You don't need to use $ as the main object.
<script src="myapp.js"></script>
<script src="homepage.js"></script>
<-....->
<script>
myApp.homepage.start();
</script>
Would be the way I would use the homepage object.
When compressing with YUI, you should have:
<script src="scripts.min.js"></script>
<script>
myApp.homepage.start();
</script>
Just to make sure I've understood you correctly, you have one js file with all your code, but you want to still be in control of what is executed on a certain page?
If that is the case, then the Terrific JS framework could interest you. It allows you to apply javascript functionality to a module. A module is a component on your webpage, like the navigation, header, a currency converter. Terrific JS scans the dom and executes the js for the modules it finds so you don't have to worry about execution. Terrific JS requires OOCSS naming conventions to identify modules. It's no quick solution to your problem but it will help if you're willing to take the time. Here are some more links you may find useful:
Hello World Example:
http://jsfiddle.net/brunschgi/uzjSM/
Blogpost on using:
http://thomas.junghans.co.za/blog/2011/10/14/using-terrificjs-in-your-website/
I would use something like YUI compressor to merge all files into one min.js file that is minified. If you are looking for performance both merging and minifiying is the way to go. http://developer.yahoo.com/yui/compressor/
Example:
Javascript input files: jquery.js, ads.js support.js
run yui with jquery.js, ads.js, support.js output it into min.js
Javascript output files: min.js
then use min.js in your html code.

Categories

Resources