We built a widget app with backbone and require.js. It works cool with one app instance on page. Now we have a new requirement. We need to run multiple widget instances on same page. Each of app will have its own configurations.
The following test code is not working as we expected:
for(var i=0;i<3;i++){
require([ "app" ], function(app) {
var bootstrap = {};
jQueryPB(function() {
app.testData = i;
app.startup();
});
});
}
I wonder how could I instantiate multiple apps and set different configs to them?
Project structure is similar to:
/main.js
require.config({
....
});
/*code to create multiple instances*/
require(["app"], function(app){
var instance = new app(color:"yellow");
var instance2 = new app(color:"red");
instance.render();
instance2.render();
/*want to create multiple instances here to same page*/
})
/*above code is not working, TypeError: app is not a constructor*/
/app.js
define([ "jQueryPB", "backbone", "underscore", "models/app", "views/app" ], function($jpb,
Backbone, _, appModel, appView) {
var appInfo = new appModel();
var app = new appView({
model : appInfo
});
return app;
});
/models/app.js
/views/app.js
/view/bags.js
/view/bag (it references app by var app = require("app") , so that it can access app.color)
/collection/bags
/model/bag
I use r.js to compile all js into one
node r.js -o build.js optimize=none
After main.js is fully downloaded, it would start to initialize different app instances.
=================================================updated code
cool. I tried it with similar way:
main.js
require(["app"], function(app){
var instance = new app({
testData : 1
});
instance.testData = "1";
instance.startup();
})
app.js
define([ "jQueryPB", "backbone", "underscore", "models/app", "views/app" ], function($jpb,
Backbone, _, appModel, appView) {
return function app(color) {
var appInfo = new appModel();
var app = new appView({
model : appInfo
});
console.log(">>"+color.testData);
app.testData = color.testData;
return app;
};
});
a problem is in bag.js, it needs to access the custom variable in app. I use var app = require("app"); console.log(app.testData); But the output is undefined. Is there a different way to access app instance?
In addition, if require("app"), will it cause a problem if there are multiple app instances?
The problem is, require being an asynchronous function, does not execute immediately, and when it finally does execute, the value of i will have changed. Ultimately, you will end up with all your instances sharing the same value of i, as the loop will have completed before the first require callback gets executed. See here.
You can solve this by creating a closure around each iteration of your loop. This way the original value of i is retained within the scope of the require callback.
for (var i = 0; i < 3; i++) {
(function (i) {
require(["app"], function (app) {
// etc
});
})(i);
}
Related
We have an SPA application with a shared scope variable var WIDGET. Each module adds itself to the shared WIDGET object or creates the WIDGET if it was not created.
Using a couple of frameworks for unit tests the shared WIDGET is available but the variable on the required files do not point to it.
var WIDGET = WIDGET || {};
if I change this line of code to WIDGET = WIDGET || {} or if I remove it all together then the objects will work as expected in the unit test
things to know:
we do NOT use nodejs in production, it is just for testing
code executes properly in production
test frameworks have been mocha and jest
Q1 what can I do to ensure the private variable is pointing to the shared object when unit testing?
Q2 why does this happen in testing but not in the DOM during application execution?
test application creation:
TypeError: Cannot read property 'getInstance' of undefined
at new widget_application (widgetApplication.js:23:41)
at Object.getInstance (widgetApplication.js:33:16)
at Context.<anonymous> (test\testApp.js:16:39)
first module that adds itself to the WIDGET object
var _WIDGET_ = _WIDGET_ || {};
//adds itself to the _WIDGET_ to be shared
_WIDGET_.widgetLogger = (function(){
var INSTANCE;
var _config = {};
var _widgetLog = {};
function widget_logger(config){
if(config){_config = config;}
}
widget_logger.prototype.stageLog = stageLog;
// widget_logger.prototype.startUpLog = startUpLog;
// widget_logger.prototype.dataRefreshLog = dataRefreshLog;
// widget_logger.prototype.widgetNotReadyLog = widgetNotReadyLog;
// widget_logger.prototype.finalizeLog = finalizeLog;
// widget_logger.prototype.getLog = getLog;
return {
getInstance:function(c){
if(!INSTANCE){
INSTANCE = new widget_logger(c);
}
return INSTANCE;
}
};
function stageLog(data) {
console.log("widget logger called to stage log");
if(data){
_widgetLog = {};
_legacyLog = {};
_widgetLog.widgetProgramCode = _config.versionData.programCode;
_widgetLog.widgetVersion = _config.versionData.version;
_widgetLog.startTime = data.startTime || new Date();
console.log("widget logger staged this log ",_widgetLog);
}
}
})();
//export is only for testing - nodejs NOT used in production
var module = module || {};
module.exports = _WIDGET_;
next module that adds itself and uses the logger module
var _WIDGET_ = _WIDGET_ || {};//remove this line of code and unit test run
var _API_ = _API_ || {};
_WIDGET_.smallApplication = (function(){
var INSTANCE;
var widgetLogger;
var _config = {
versionData:{
programCode:"smallApplication",
version:"1.0.2.0"
}
};
function widget_application(config){
console.log("make the instance ");
// var elem = document.getElementById('apprunning');
// elem.innerHTML += " smallApplication is online ";
if(config){_config = config;}
//expects to have a shared object already
//fails after this call because this module create a new _WIDGET_ variable
//this does not happen in production - all components are created
widgetLogger = _WIDGET_.widgetLogger.getInstance(_config);
}
widget_application.prototype.runApplication = runApplication;
return {
getInstance:function(c){
console.log("get instance was called");
if(!INSTANCE){
INSTANCE = new widget_application(c);
}
return INSTANCE;
}
};
//run some functions and hello world and all that happiness...
function runApplication(){
console.log("widget application runs - will call logger");
widgetLogger.stageLog(true);
}
})();
//export is only for testing - nodejs NOT used in production
var module = module || {};
module.exports = _WIDGET_;
mocha test script
var assert = require('assert');
_WIDGET_ = {name: 'small application'};
describe('small application',function(){
//_WIDGET_ = {name: 'small application'};
//var _WIDGET_ {name: 'small application'};
//global._WIDGET_ {name: 'small application'};
it('test logger creation',function(){
_WIDGET_ = require('../widgetLogger');
console.log('_WIDGET_');
});
it('test application creation',function(){
var app = require('../widgetApplication');
_WIDGET_ = require('../widgetApplication');
});
});
A2 to Q2 found at free code camp - require modules In a browser, when we declare a variable in a script like this:
var answer = 42;
That answer variable will be globally available in all scripts after the script that defined it.
This is not the case in Node. When we define a variable in one module, the other modules in the program will not have access to that variable. So how come variables in Node are magically scoped?
The answer is simple. Before compiling a module, Node wraps the module code in a function, which we can inspect using the wrapper property of the module module.
Big Thanks to free code camp
They made it simple to see that node.js require('module') wraps the module in a function().
That helped me to realize I needed to concat my modules into 1 file for testing. Then require('minifiedModules')
in my unit tests the code looks like
var _Widget_ = require('minifiedModules');
var app;
test('verify _Widget_',function(){
//testing with jest
expect(_Widget_.widgetLogger).toBeDefined();
expect(_Widget_.smallApplication).toBeDefined();
});
test('instantiate app',function(){
app = _Widget_.smallApplication.getInstance();
});
test('run app',function(){
app.runApplication();
});
and running npm test now works
> jest
PASS ./app.test.js
√ _WIDGET_ objects (34ms)
√ instantiate app (5ms)
√ run app (5ms)
console.log app.test.js:23
Widget objects { widgetLogger: { getInstance: [Function: getInstance] },
smallApplication: { getInstance: [Function: getInstance] } }
console.log test/scripts/services/services.min.js:87
get instance was called
console.log test/scripts/services/services.min.js:71
make the instance
console.log test/scripts/services/services.min.js:78
no way this works, _WIDGET_, { widgetLogger: { getInstance: [Function: getInstance] },
smallApplication: { getInstance: [Function: getInstance] } }
console.log test/scripts/services/services.min.js:97
widget application runs - will call logger
console.log test/scripts/services/services.min.js:30
widget logger called to stage log
console.log test/scripts/services/services.min.js:40
widget logger staged this log { widgetProgramCode: 'smallApplication',
widgetVersion: '1.0.2.0',
startTime: 2018-10-30T03:41:10.815Z }
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 2.067s
Ran all test suites.
I'm using the single-page app template from https://github.com/volojs/create-template
I tried to make a simple example below.
Problem
ModuleA.js is being loaded twice, once directly from main.js and again from simulator.js which depends also on that module. This is causing two different references to an object (which I thought would only be one, like a singleton). I thought requirejs would not load the same module twice. Being (relatively) new to JavaScript I realize this may be naivete on my part. I'm trying to follow the template.
Here's a simplified version that demonstrates the problem:
www/index.html
<head>
...
<script data-main="app" src="lib/require.js"></script>
</head>
...
www/app.js
// For any third party dependencies, like jQuery, place them in the lib folder.
// Configure loading modules from the lib directory,
// except for 'app' ones, which are in a sibling
// directory.
requirejs.config({
baseUrl: 'lib',
paths: {
app: '../app'
}
});
// Start loading the main app file. Put all of
// your application logic in there.
requirejs(['app/main']);
www/app/main.js
define(function (require) {
var simulator = require('./simulator.js');
var ModuleA = require('./ModuleA.js');
ModuleA.init();
ModuleA.displayList();
ModuleA.update("joe", 99);
ModuleA.displayList();
simulator.start(); // should display the same list
});
www/app/ModuleA.js
define(function () {
var theList = {};
console.log("loading ModuleA");
return {
displayList: function () {
console.log(Object.keys(theList));
},
init : function () {
theList["fred"] = 10;
},
update : function (k, v) {
theList[k] = v;
}
}
});
www/app/simulator.js
define(["./ModuleA"], function (ModuleA) {
return {
start: function () {
ModuleA.displayList();
}
};
});
console output:
loading ModuleA
loading ModuleA
["fred"]
["fred", "joe"]
[]
The empty [] displayed on the last line is (likely) the second copy of the list due to the second loading of ModuleA.
The problem is the module references are not consistent. In main.js it requires ./ModuleA.js whereas in simulator.js it defines ./ModuleA (without the .js filetype).
Making those references identical corrects the behavior such that the module is only loaded once.
I guess I mixed the styles because of the many examples on the web. It kind of seems like a bug that it works this way, but maybe it's a feature?
If you want to share an instantiated singleton object using requireJS, you can do something like this for ModuleA:
define(function () {
console.log("loading ModuleA");
function myList(){
this.theList = {}
}
myList.prototype.displayList = function () {
console.log(Object.keys(this.theList));
}
myList.prototype.init = function () {
this.theList["fred"] = 10;
}
myList.prototype.update = function (k, v) {
this.theList[k] = v;
}
return new myList();
});
I would like to attempt to build a larger, more modular node.js application than my previous attempts. I am stuck on design patterns as it appears there are way too many ways to accomplish the same thing, and I am lost in the blog FUD around how to accomplish this. I have worked mainly in MVC frameworks where a global app variable is passed around, and I am wondering if this concept is useful in node. For example:
./index.js
var app = {};
var api = require('api'); // Api server module
var db = require('db'); // db client
var dbh = db.connect(); // connect to db
app['dbh'] = dbh;
api.start(app);
./api.js
var API = exports;
API.start = function(app, callback) {
// do api server stuff
app['dbh'].execute(... function (err, results){
console.log('foo');
});
}
Is this idea of assigning all functions, configuration data, and variables to an app object a reasonable approach?
You can make a minor adjustment for it to work:
// api.js
module.exports.start = function(app, callback) {
// do api server stuff
app['dbh'].execute(... function (err, results){
console.log('foo');
});
}
I think you need to know how to pass some objects between Node modules. Below is what I do in this case:
Your main module that contains the app object that we'll pass around:
// index.js
// this module immediately invokes itself and requires our other modules
module.exports = (function () {
'use strict';
// this is the main object I want to use in other modules
var app = {
version: '1.0.0'
};
// just requiring and invoking module2 with the app argument
require('./module2')(app);
// now, I'll set a reference to module3 bec.
// I'll also need module3 in module4 below
var module3 = require('./module3')(app);
// and here I invoke module4 with 2 arguments this time.
require('./module4')(app, module3);
// so on...
return app;
}());
Now in your secondary modules, you can use the passed object(s):
// module2.js and/or module3.js
// notice that the module is invoked with the app object.
module.exports = function (app) {
'use strict';
console.log(app.version);
return {
message: 'Hi there!'
};
};
// module4.js
// notice that this module is invoked with 2 arguments this time.
module.exports = function (app, module3) {
'use strict';
console.log(app.version); // '1.0.0'
console.log(module3.message); // Hi there!
};
Hope this gives you an idea.
I’m writing jasmine tests for an ExtJS 4.1.1 client app. The server side code is Java. The client app doesn’t follow a strict MVC pattern; the models are separate but the view and controller code is co-mingled. The controllers don’t even inherit from Ext.app.controller. (Don’t blame me, I didn’t write it).
I’m able to test the Ext.app.Application object but keep getting null whenever I try to create a custom ExtJS layout object inside the Jasmine spec.
It's probably a basic error in my test ExtJS app or jasmine spec but I'm very new to ExtJS and can't pin point it.
Any help is really appreciated.
File structure:
/myapp (java code)
...
/webclient
/assets
/extjs-4.1.1
/myapp (same name as top level java code)
myapp.js
myapp-ui.js
/myapp-test
runspec.html
/lib
/jasmine-2.3.4
/specs
uispecs.js
/ui
/layouts
myappgrid.js
myapppanel.js
Here's the code for the ExtJS app (myapp-ui.js), Jasmine specs (uispecs.js), and spec runner (runspec.html)
myapp-ui.js
Ext.Loader.setConfig({
enabled : true,
paths : {
myapp : 'myapp'
}
});
var Application = null;
Ext.onReady(function() {
Application = Ext.create('Ext.app.Application', {
name: 'myappUI',
appFolder: 'myapp',
autoCreateViewport: true,
launch: function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.execute();
}
});
});
Here's the code for uispecs.js (Jasmine test spec)
describe("Basic Assumptions", function() {
var myLayout = null;
beforeEach( function() {
// This always returns null
myLayout = Ext.create('myappUI.ui.layouts.MyLayout');
});
// This test PASSES
it("myappUI is defined",function() {
expect('myappUI').toBeDefined();
});
// This test FAILS: myLayout is never created
it ('myLayout shouldn’t be null', function () {
expect ( myLayout != null).toBeTruthy();
});
});
I'm using Require.js and Backbone, and have a Backbone router module like:
define([
"views/global",
"views/project/edit",
"views/project/list",
], function(GlobalView, edit, list){
return Backbone.Router.extend({
routes: {
"projects/:action/" : "projectsAction",
},
projectsAction : function(action) {
/* .... lots of code cut out here .... */
/* Create and render the action specified */
this.subView = new eval(action+"()").render();
}
});
});
This is an example, I've cut a lot of setup code out of projectAction.
I would like the URL: /projects/list to run projectAction, with the action param = list, and then the list module from the Require.js function to be called. I'm currently doing it with eval(), but I'm wondering if there is a better way?
Basically, in Javascript, can you refer to a variable, with another variable name, without using eval()?
I guess a shorter version would be, how do you do:
var name = "Math.random";
name(); // = 0.34343....
Without eval()?
You cannot access a variable having the name in a string. But you can create a mapping:
var actions = {
edit: edit,
list: list
};
And then you can access the function by the key:
projectsAction : function(action) {
this.subView = new actions[action]().render();
}
The best way imo, is to use the require function of requirejs:
projectsAction : function(action) {
/* .... lots of code cut out here .... */
/* Create and render the action specified */
var self = this;
require('views/project/' + action, function(view) {
(self.subView = new view).render();
}
}
As it would also cut the boilerplate from having lots of actions.