How do you load a javascript file into a scope - javascript

I'm trying to figure out how to load the contents of a JavaScript file into a scope. The reason for this is I want to be able to have plug and play capability and be able to load different functionality into my app. Perhaps the user role determines what functionality is loaded, or perhaps I want to tailor my app for a given client.
Here's an example of the contents of one of my pluggable JavaScript file:
myApp.LoginViewModel = (function ()
{
//DATA
var self = this;
return {
HelloWorld: function ()
{
alert('Hellow World from Login View Model');
}
};
})();
Here is an example of the configuration file listing what JS files should be loaded:
var myApp = myApp || {};
myApp.ViewModelRegistry =
{
BaseViewModel: { JavaScriptPath: "/Scripts/ViewModels/BaseViewModel.js"},
LoginViewModel:{ JavaScriptPath: "/Scripts/ViewModels/LoginViewModel.js"}
};
Here's my code that loops through ViewModelRegistry and uses jQuery to load the script:
InitializeApp: function ()
{
//REGISTER VIEW MODELS
for (var viewModelName in myApp.ViewModelRegistry)
{
var registeredModel = myApp.ViewModelRegistry[viewModelName];
$.getScript(registeredModel.JavaScriptPath, function (data, textStatus, jqxhr)
{
});
}
console.log(myApp);
}
};
My goal is to load the JavaScript function from the JS file into the myApp scope. Each pluggable JS file is scoped to myApp. I thought simply using jQuery's getScript to load the JS file would automatically update the myApp scope since all pluggable JS files are designed to that scope. However, the console.log(myApp) doesn't reveal the code from the JS files. What am I missing? Is this even feasible?

I believe your console.log is executed before any of your JS files have been retrieved and parsed. Other than that, you seem to be on the correct course. This is definitely feasible, and the terminology used to describe what you want to achieve is called Asynchronous Module Definitions.

Related

Amd, requirejs - want to ensure something always executes last

I'm trying to avoid global scope for the following problem, but cannot see a way to avoid it.
I have a singleton Javascript object called "Application". This is an AMD module.
I then have many "Modules" (not to be confused with AMD modules) which are just javascript objects that I'd like to register with the "Application" instance.
For example:
require(['Application'], function(app) {
var module = {
name: "theme-switcher",
start: function() { console.log("started") } }
app.registerModule(module)
}
The architecture I am going for, is i'd like for each "Module" on the page to register itself with the "Application" instance.
Here is the tricky part: Only once ALL modules have been registered with the application, do I then want the "Application" instance, to loop through those registered modules and call their "Start()" methods.
The way I thought to do this was to just add another require block at the bottom of the page like this:
<script type="text/javascript">
require(['Application'], function (app) {
// start all the registered modules running.
app.start(() => {
// this could be a call back function once all modules started..
});
});
Niavely thinking, that just because this require call was last, that it would allways be executed last. But actually, sometimes this gets fired BEFORE the require calls above - so the Application attempts to Start() all registered modules BEFORE the modules themselves have all registered themselves with the Application.
No matter how I think about this problem, I am often led back to the fact that I need to keep some state in global scope, Something like this:
Module 1:
var app = Application.Instance;
var moduleStart = function(){
require(['jquery'], function(jquery) {
// do module goodness here.
}};
app.registerModule({name: "theme-switcher", start: moduleStart })
// later on in page - some other widget
// Module 2
var app = Application.Instance;
var moduleStart = function(){
require(['jquery'], function(jquery) {
// do second module goodness here.
}};
app.registerModule({name: "foo", start: moduleStart })
And then at the bottom of the page,
var app = Application.Instance;
app.Start(); // loops through registered modules calling start() method.
Surely there must be a way to do this avoiding global scope?
The reason for me wanting to do this, is that I want the "Application" to manage the lifecycle for registered modules on the page - including starting / pausing / stopping them etc. I'd also like the Application to publish an event once ALL modules gave been started - as this is when I would typically stop displaying my "loading" animation, and actually display the DOM - as modules will often manipulate the DOM in their start() methods and I don't want the page to be visible before everything is started.
This will do it. If you make every object you are wanting an AMD module, which I think you should be doing if you have RequireJS in place anyway, then you'll just need an array of strings defining those AMD module names to pass as an argument to the app's init.
Application.js : -
define(function (require, exports, module) {
"use strict";
var modules = {};
var moduleNames = [];
var numberOfModules = 0;
var loadedModules = 0;
exports.init = function (dependencies) {
numberOfModules = dependencies.length;
for (var i = 0; i < numberOfModules; i++){
var name = dependencies[i];
moduleNames.push(name);
require([name], function (moduleRef) {
loadedModules++;
modules[name] = moduleRef;
if (numberOfModules === loadedModules) {
exports.start();
}
});
}
};
exports.start = function () {
// all modules available
// use modules.myModuleName to access the module.
modules.myModuleName.functionName();
// or if they all have start() function and it needs calling
for (var i = 0; i < moduleNames.length; i++) {
modules[moduleNames[i]].start();
}
};
});
USAGE Depending on how you are loading your app, assuming you have an Application reference somewhere, just call:
// names of modules RequireJS uses to require, can be changed for each page.
var dependencies = ['moduleOne', 'moduleTwo', 'myModuleName'];
app.init(dependencies);
CodePen of this code, slightly altered to work on one page... http://codepen.io/owenayres/pen/MyMJYa

Best way to share an instance of a module that is inside a closure [duplicate]

If I want to span my JavaScript project across multiple source files, but have each file have access to the same private variable, how would one do that?
For example, if I have the following code:
APP = (function () {
var _secret = {},
app = {};
// Application part 01:
app.part01 = (function () { /* function that uses _secret */ }());
// Application part 02:
app.part02 = (function () { /* function that uses _secret */ }());
//
return app;
}());
How do I put app.part01 and app.part02 in seperate files, but still have access to _secret?
I don't want to pass it as an argument. That's just giving the secret away, as app.part01() could be replaced by any other function.
Maybe I am asking the impossible, but your suggestions might lead me in the right way.
I want to work with multiple files, but I don't know how. Copying and pasting everything inside a single function each time before testing is not something I want to do.
How do I put app.part01 and app.part02 in seperate files, but still have access to _secret?
That's impossible indeed. Script files are executed in the global scope, and don't have any special privileges. All variables that they will be able to access are just as accessible to all other scripts.
Copying and pasting everything inside a single function each time before testing is not something I want to do
What you are looking for is an automated build script. You will be able to configure it so that it bundles your files together, and wraps them in an IEFE in whose scope they will be able to share their private state. The most simple example:
#!/bin/sh
echo "APP = (function () {
var _secret = {},
app = {};" > app.js
cat app.part01.js >> app.js
cat app.part02.js >> app.js
echo " return app;
}());" >> app.js
The only way that you can share _secret is attaching it to the application object and then application object to the window object. Here is an example.
// FIRST JS FILE...
var application; // will be attached to window
(function(app) {
app.secret = "blah!"; // will be attached to application
})(application || (application = {}));
// ANOTHER JS FILE
var application;
(function(app) {
app.method1 = function(){ console.log(app.secret); }; // will be attached to application;
})(application || (application = {}));
console.log(application.method1()); // will display 'blah!' on the console
Working example on jsbin
One way I was able to accomplish this was to create a JS file that contained the global object.
// Define a global object to contain all environment and security variables
var envGlobalObj = {
appDatabase: process.env.YCAPPDATABASEURL,
sessionDatabase: process.env.YCSESSIONDATABASEURL,
secretPhrase: process.env.YCSECRETPHRASE,
appEmailAddress: process.env.YCAPPEMAILADDRESS,
appEmailPassword: process.env.YCAPPEMAILPASSWORD
}
module.exports = envGlobalObj
Then in the files I wish to reference this object, I added a require statement.
var envGlobalObj = require("./envGlobalObj.js");
This allowed me to centralize the environment and secrect variables.

Using a RequireJS module more than once does not run

Running require(['pages/home']) will work once but if I use require(['pages/home']) again then it won't run.
The module "pages/home" is a file named "home.js" in a directory named "pages".
main.js
require(['pages/home']);
pages/home.js
define('pages/home', function() {
console.log('running pages/home module');
});
RequireJS modules are singletons. It loads a module once and only once. If a module has been loaded already, what you get if you load it again is a reference to the same module as originally loaded. The factory function you pass to define won't be run a second time.
So what you are seeing is exactly what is expected.
Static code in modules isn't supposed to be evaluated more than once, just like a script loaded through a normal <script> tag won't be run more than once during the page load.
Imagine if a module contained code like:
define('my-module', function () {
var foo = foo || 0;
var bar = ++foo;
});
You should expect bar and foo to both === 1, but if the module was run repeatedly and a global foo existed, that may not be the case. Admittedly, this is a very contrived example, but evaluating a module repeatedly could cause serious problems.
Make it return a function/object that can be executed after you require it.
define('pages/home', function() {
return function(){
console.log('running pages/home module');
};
});
require(['pages/home'], function(resultFunc){
window.YourFunc = resultFunc;
});
Now you can execute your function whenever you want

Javascript - accessing namespace in different files

I can do this in node.js
var module = require('../path/module')
module.functionname()
So I thought I'd like to do that in client side Javascript to organize things slightly.
So each of my files now has a namespace. So say login.js has a namespace login.
My question is, what's the best way in ECMAScript 5 to implement something alongs these lines?
What you are trying to achieve can be done with AMDs (asynchronous module definitions). Check out RequireJS for that: http://requirejs.org/
With Require.js you can basically define a set of dependencies, let them get loaded asynchronously and execute code once all stuff was loaded nicely:
require(['dependency1.js', 'dependency2.js'], function(dep1, dep2) {
console.log(dep1.functionname())
})
The dependency will then declare it's functionalities with require's define method:
define(['a/possible/dependency.js'], function() {
return {
functionname: function() { return 1 }
}
})

What is the preferred method for passing server data to a RequireJS module?

Is there a preferred way to pass server data in a RequireJS module? Our current implementation looks like the following code snippets; using a 'page' object to hold any server/dynamic data and passing that to the main bootstrap. (We don't want to use ajax to populate any dependencies at this time)
From a server page :
<script data-main="scripts/main" src="scripts/require-jquery.js"></script>
<script type="text/javascript">
define("page", function () {
return { guid: "<%=Guid.NewGuid() %>" };
});
</script>
main.js
require(["jquery", "jquery.alpha", "page"], function ($, alpha, page) {
alpha.initialize(page);
});
jquery.apha.js
define(["jquery", "page"], function ($, page) {
return {
initialize: function () {
console.log(page.guid);
//logs guid as expected
}
}
});
I usually do something like this (using PHP on the back-end but anything works):
<script src="scripts/require-jquery.js"></script>
<script>
require(['scripts/main'], function(App) {
var myApp = new App({
param1: <?=json_encode($param1);?>,
param2: <?=json_encode($param2);?>
});
});
</script>
And then define my module as something that takes a config:
define(['jquery'], function($) {
var App = function(options) {
this.options = options;
//blabla
}
// add some stuff to App.prototype maybe
// and finally...
return App;
});
RequireJS says nothing about how to deal with server data, as it is a means to modularize your javascript. So in that regard there is no defacto standard and you can combine RequireJS with json, ajax, php, embedded xml etc however you want.
Two Approaches
There generally are two ways to go about this.
Model a 'dao' or 'service' module that gets the required data from the server and
makes it accessible to its users (similar to your current approach, see code sample below)
Define a global object to which all modules have access
The first approach adds parameters to your functions.
The second provides global access. This also requires your own initialization code to start fetching data.
It comes down to personal preference and how many of these 'dao's' you have. If you have more than one it might become poluting as you need a new parameter for each dao module. In that case making them global seems cleaner.
A problem with your approach
There is a problem with your current approach though, where you have the Page module as a definition (using define() instead of require()), because a define module is created for each object that depends on it. This potentially means multiple calls within the same page. Instead use:
// in seperate file page.js:
require([], function () {
return { guid: "<%=Guid.NewGuid() %>" };
});
This way RequireJS recognizes page as a module because it is a seperate file and it will go to your server only once per page.
If you have a JSON object, make an AJAX call like #yves mentioned in the comments.
There are other options if you don't want to do that. You could put the guid as a data attribute on the script tag. Also, you could try making the loader js file dynamic so the config is set in that.
Honestly though, I'd just make an AJAX call.
I just started today with RequireJS and prior to this I was used to just call the function I wanted to execute on page load like this:
<script>
my_method(<?php echo json_encode( array('opt1'=>true, 'opt2'=>false) );?>);
</script>
As #ziad-saab I've found that the most similar thing I can do is not using the data-main attribute and just define an inline module:
<script src="path/to/require.js"></script>
<script>
require(['my/module'],function(module){
module.my_method(<?php echo json_encode( array('opt1'=>true, 'opt2'=>false) );?>);
});
</script>
The data-main attribute instructs RequireJS to execute the module as soon as require.js and all module dependecies are loaded. Omitting it (the module) and just defining it as an inline module I'm able to throw in PHP variables.
This way I don't need to handle with modules that hold my configurations and the transition to use requirejs is easier in my environment.
I have found some of the answers confusing, so here are the exact steps you need to follow to make it work for you:
In my case I am doing this like so:
index.php
<script src="/js/crm/lib/require.js"></script>
<script>
// this is so called "named define"
define('dto', {
page: JSON.parse('{{ pageDTO | json_encode }}'),
flashMessages: JSON.parse('{{ this.flashSession.getMessages() | json_encode }}')
});
// note we are using relative path to public dir here
// order is also important, we need to define our dto module before bootstraping the application
require(['/js/crm/app.js']);
</script>
app.js
"use strict";
require.config({
// ...
baseUrl: '/js/crm/lib',
paths: { app: '../app' }
});
require(['app/bootstrap']);
some-module.js
(in this case layout.js that is required in app/bootstrap)
"use strict";
define([
'dto',
'jquery',
'lodash'
], function (dto, $, _) {
console.log(dto);
});
Note using data-main to bootstrap the application, without explicit call to require might work, but due to race condition. If defining dto for some reason would take more than it takes requirejs to call main module script will crash. We don't want to rely on that, so we do everything ourselves :)
So this would not work (sometimes):
<script data-main="/js/crm/app.js" src="/js/crm/lib/require.js"></script>
<script>
// this is so called "named define"
define('dto', {
page: JSON.parse('{{ pageDTO | json_encode }}'),
flashMessages: JSON.parse('{{ this.flashSession.getMessages() | json_encode }}')
});
</script>
Use window global variable to transfer server data into js application:
<script type="text/javascript">
window.server_data=parseJSON(<?php echo json_encode(array ("server_data"=>"it works!"));?>);
</script>
<script data-main="js/application" src="js/lib/require.js"></script>
in application.js:
requirejs(["app/main"],function (MyApp){
console.dir(window.server_data); //all our application need this global variable
var myApp=new MyApp();
myApp.init(window.server_data); //and your application now has server data
});

Categories

Resources