How to guarantee dependent class in javascript with requirejs - javascript

I was hoping that the following code would guarantee that parentClass is loaded before childClass, and that both would be loaded before startMyApp was called.
require([
"parentClass",
"childClass"
], function (parentClass, childClass){
Main.startMyApp();
});
If not, how can I guarantee that? Main, is an object. Child class is defined as such:
var childClass = function childClass() {
this.name = 'some name';
};
childClass.prototype = new parentClass();
childClass.prototype.constructor = childClass;
And here is parentClass:
var parentClass = function parentClass() {
};
parentClass.prototype.myFunction = function myFunction(){
//do something
}
I am trying to avoid adding define's to all of my classes, I've got dozens. Is that the only way to guarantee class are available when I need them? Thanks!

The call require(["parentClass", "childClass"], ... tells RequireJS to load both modules but this call does not coerce the order in which the modules are loaded. What does coerce the order of the modules are the dependencies you establish between the modules.
Since this is your own code, and you've decided to use RequireJS, then you should write proper AMD modules. To establish dependencies you list them as the first argument of your define calls (if needed). For your parent class:
define(function () {
var parentClass = function parentClass() {
};
parentClass.prototype.myFunction = function myFunction(){
//do something
}
return parentClass;
});
For your child class:
define(['parentClass'], function (parentClass) {
var childClass = function childClass() {
this.name = 'some name';
};
childClass.prototype = new parentClass();
childClass.prototype.constructor = childClass;
return childClass;
});
Then whatever other modules needs childClass just require your childClass module and parentClass is guaranteed to be loaded before childClass because parentClass is listed as a dependency in the define call.

You want to use the shim config, for example:
require.config({
paths: {
jquery: ['../bower_components/jquery/jquery.min'],
underscore: ['../bower_components/underscore/underscore-min']
app: 'app'
},
shim: {
underscore: {
deps: ['jquery'],
exports: '_'
},
waitforimages: {
deps: ['jquery']
},
cyclotron: {
deps: ['jquery']
},
placeholder: {
deps: ['jquery']
},
app: {
deps: ['jquery', 'underscore', 'fastclick', 'spinjs', 'waitforimages', 'backgroundCheck', 'raphael', 'swipe', 'history', 'cyclotron', 'placeholder']
}
}
});
require([
'app'
]);
this isn't the most optimized example, but basically, if you say that something is a dep of another script, it will make sure to load those files. So you can see in this example, I am telling require that these plugins require jquery, and my app requires jquery and these plugins.
This way everything gets loaded before my app.js

Related

Request specific RequireJS configuration context

If I configure a specific context:
var myRequire = require.config({
context: 'foo',
paths: {
'jquery': 'jquery-1.11.2'
}
});
I can then immediately use myRequire to load a module:
myRequire(['require', 'jquery'], function (require, $) { /* ... */ });
But if I move the configuration to a separate file, how do I retrieve the proper context?
Based on this answer I found that requirejs.s.contexts['foo'].require returns the same function as myRequire. That seems hacky. Another option would be to define a module for the config and return myRequire from it. What is the approved method?
This is working:
require.config.js:
define(function () {
'use strict';
var myRequire = require.config({
context: 'foo',
paths: {
'jquery': 'jquery-1.11.2'
}
});
return {
myRequire: myRequire
};
});
main.js:
// does need path and .js extension
requirejs(['/scripts/require.config.js'], function (requireConfig) {
var myRequire = requireConfig.myRequire;
myRequire(['require', 'jquery'], function (require, $) { /* ... */ });
});
I'm still open to better ways, and I have not tested this with r.js.

Strange Requirejs behavior when loading Markdown

I have a requirejs module in which I am trying to load markdownjs. Here is the file:
define(function(require) {
'use strict';
var Backbone = require('backbone');
var blogCollectionTemplate = require('hbs!app.templates/blog.collection.view');
var BlogModelView = require('views/blog.item.view');
var markdown = require('markdown');
var BlogCollectionView = Backbone.View.extend({
template: blogCollectionTemplate,
initialize: function() {
debugger;
},
render: function() {
this.$el.html(this.template());
this.renderAll();
return this;
},
renderAll: function() {
var that = this;
this.collection.each(function(blog) {
that.renderItem(new BlogModelView({model: blog}));
});
},
renderItem: function(blog) {
this.$el.find('#blog-posts').append(blog.render(blog).el);
}
});
return BlogCollectionView;
});
Here is my require.config:
define(function() {
require.config({
hbs : {
templateExtension : 'hbs',
disableHelpers: true,
disableI18n : true
},
shim: {
'backbone': {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
bootstrap: {
deps: [ 'jquery' ]
},
DlHighlight: {
exports: 'DlHighlight'
},
'jqueryMockAjax': {
exports: '$.mockjax',
deps: ['jquery']
},
json2 : {
exports: "JSON"
},
'underscore': {
exports: '_'
}
},
paths: {
backbone: 'libs/backbone/backbone',
bootstrap: 'libs/bootstrap/dist/js/bootstrap',
DlHighlight: 'libs/hl/hl-all',
highlight: 'libs/highlightjs/highlight.pack',
jquery: 'libs/jquery/jquery',
jqueryMockAjax: 'libs/jquery-mockjax/jquery.mockjax',
markdown: 'libs/markdown/lib/markdown',
text: 'libs/text/text',
underscore: 'libs/underscore/underscore',
hbs: 'libs/hbs/hbs',
handlebars: 'libs/hbs/Handlebars',
i18nprecompile: 'libs/hbs/hbs/i18nprecompile',
json2 : 'libs/hbs/hbs/json2',
'app.templates': '../templates/'
}
});
});
Here is the strange behavior. In my initialize, when I hit the debugger, I have access to the markdown object that I have imported, BUT if I have try to use the markdown object, then it is always undefined. If I put markdown in the initialize or in one of the render methods, the markdown variable is undefined. It makes no sense, but is there some behavior that I dont understand about requirejs. Any ideas?
After reading the code of a bower installation of markdown-js, I found that what bower installs won't work with RequireJS as-is. Try adding this shim:
"markdown": {
exports: "markdown"
}
As to why were you able to get a value for markdown in the debugger without the shim, I believe you were getting it (perhaps without realizing it) from the global scope. Markdown-js installs itself into the global scope (window.markdown, which is then accessible as markdown if no other variable interferes with it) when it is loaded. This is speculation but it fits the facts.
You can require all of those modules in the define clause itself:
define([
'backbone',
'hbs!app.templates/blog.collection.view',
'views/blog.item.view',
'markdown'
], function (
Backbone,
blogCollectionTemplate,
BlogModelView,
markdown
) {
'use strict';
// do stuff
});
Also, what do you mean by "If I put markdown in the initialize or in one of the render methods"? Do you mean actually explicitly requiring markdown in initialize and render? Is there any reason to not just load markdown in the define clause as labeled above?
If you are explicitly requiring markdown in initialize or render, I am not sure why that would return undefined, but let me know if moving requirements to the define clause fixes your issue (or if you can't do that). Perhaps you could post the code in the markdown module (if it's not a library that is)?

requireJS module loading

I need some help with the concept of only loading modules when they are needed using requireJS
this is my main.js
require(['jquery', 'path/somemodule'],
function($, somemodule) {
$(document).ready(function() {
somemodule.init()
})
})
and in the somemodule.js
define(['jquery', 'path/someothermodule'], function ($, someothermodule) {
"use strict";
var somemodule;
somemodule = {
init: function () {
someothermodule.init()
}
}
return somemodule;
)}
right now somemodule.js and someothermodule.js is loaded on all pages. How do I only load it when it's needed?
When you require a module2 from module1 using the standard define() syntax module1 will not load/run until module2 has been fully loaded. That looks like this:
// inside module1
define(['module2'], function(mod2) {
// we don't get here until AFTER module2 has already been loaded
});
An alternative to lazy-load module2 looks like this:
// inside module1
define([], function() {
require(['module2'], function(mod2) {
// we don't get here until AFTER module2 has already been loaded
});
// but we DO get here without having loaded module2
});
Now you have to program somewhat carefully to make sure you don't run into any issues with asynchronicity.
In your case you can modify your main.js file
require(['jquery'],
function($) {
// jquery is loaded, but somemodule has not
if(thisPageNeedsSomeModule) {
require(['path/somemodule'],
function(somemodule) {
// now somemodule has loaded
$(document).ready(function() {
somemodule.init()
})
});
}
})
Your main.js file will load any file paths provided to it, so long as other elements of your application specify them as dependencies. See my example main.js file:
require.config({
paths: {
'app': 'app',
'underscore':'bower_components/underscore/underscore-min',
'backbone':'bower_components/backbone/backbone-min',
'marionette':'bower_components/backbone.marionette/lib/backbone.marionette.min',
'jquery': 'bower_components/jquery/jquery.min',
'tpl':'bower_components/requirejs-tpl/tpl',
'bootstrap':'bower_components/bootstrap/dist/js/bootstrap.min',
'leaflet':'bower_components/leaflet/leaflet',
'leaflet.markercluster':'bower_components/leaflet/leaflet.markercluster',
},
shim: {
'underscore': {
exports: '_'
},
'leaflet': {
exports: 'L'
},
'leaflet.markercluster': {
deps: ['leaflet']
},
'backbone': {
deps: ['underscore']
},
'marionette': {
deps: ['backbone']
},
'jquery': {
exports: '$'
},
'bootstrap': {
deps: ['jquery']
},
'app': {
deps: ['jquery', 'leaflet','bootstrap', 'leaflet.markercluster', 'marionette', 'tpl']
},
'app.elem': {
deps:['app']
},
'app.api': {
deps:['app']
}
}
})
require(['app','app.api','app.elem'], function() {
App.start();
})
And my initial application file:
define(['router', 'collections/moments'], function(router, momentCollection) {
// Boot the app!
App = new Marionette.Application();
App.LocResolve = false; // Have we resolved the user's location?
App.Locating = true; // Are we actively tracking the user's location?
App.FileReader = window.FileReader ? new FileReader : null;
App.Position = null; // Instant access to Lat & Lng of user.
App.MomentsRaw = null; // Keep cached copy of returned data for comparison.
App.Moments = new momentCollection; // Current collection of moments.
App.Markers = new L.MarkerClusterGroup(); // Create Marker Cluster Group
App.View = null; // Current view.
// Marionette Regions
App.addRegions({
header: '#header',
map: '#map',
list: '#list',
modal: '#modal',
});
return App
})
I noticed that you aren't passing in a configuration object - is this intentional? If you use R.js, the build optimizer, it will automatically remove unused vendor files for you.
In short, sets paths to your vendor files in the require.js config, then call upon them via define() whenever you need a particular asset. This will ensure that only files you need are used. Hope this helps!

RequireJS + oCanvas with self-invoking functions

I have troubles with using oCanvas - it doesn't support AMD out of box so in RequireJS I define a shim:
configuration.js
require(
function () {
requirejs.config({
shim: {
'lib/ocanvas': {
exports: ['oCanvas']
}
}
});
}
);
The way I load shim-related configuration (I think it's enough to add it to require to the entry point file):
require(['configuration','main'],
function (configuration, main) {
main.startUniverse();
}
);
The problem is I cannot get the oCanvas object in my JS files:
define(['lib/ocanvas'],
function (oCanvas) {}
It appears undefined here. In oCanvas sources I see that there are 2 self-invoking functions and they put the oCanvas object into global state like that: window.oCanvas = oCanvas. Maybe this doesn't work for RequireJS?
require(
function () {
requirejs.config({
shim: {
'lib/ocanvas': {
exports: 'oCanvas'
}
}
});
}
);
Try passing it as a string not an array?
Instead of requirejs.config I now use require.config and pass an object there:
require.config({
shim: {
'lib/ocanvas': {
exports: 'oCanvas'
}
}
});
That worked for me.

Set lodash / underscore template settings globally using require.js

Is there a way to set the templateSettings for lodash when using RequireJS?
Right now in my main startup I have,
require(['lodash', 'question/view'], function(_, QuestionView) {
var questionView;
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g
};
questionView = new QuestionView();
return questionView.render();
});
but it doesn't seem to want to set the templateSettings globally because when I use _.template(...) in a module it wants to use the default templateSettings. The problem is that I don't want to change this setting in every module that uses _.template(...).
Based onĀ #Tyson Phalp suggestion, that means this SO question.
I adapted it to your question and I tested it using RequireJS 2.1.2 and SHIM configuration.
This is the main.js file, that is where the requireJS config is:
require.config({
/* The shim config allows us to configure dependencies for
scripts that do not call define() to register a module */
shim: {
underscoreBase: {
exports: '_'
},
underscore: {
deps: ['underscoreBase'],
exports: '_'
}
},
paths: {
underscoreBase: '../lib/underscore-min',
underscore: '../lib/underscoreTplSettings',
}
});
require(['app'],function(app){
app.start();
});
Then you should create the underscoreTplSettings.js file with your templateSettings like so:
define(['underscoreBase'], function(_) {
_.templateSettings = {
evaluate: /\{\{(.+?)\}\}/g,
interpolate: /\{\{=(.+?)\}\}/g,
escape: /\{\{-(.+?)\}\}/g
};
return _;
});
So your module underscore will contain the underscore library and your template settings.
From your application modules just require the underscore module, in this way:
define(['underscore','otherModule1', 'otherModule2'],
function( _, module1, module2,) {
//Your code in here
}
);
The only doubt I have is that I'm exporting the same symbol _ two times, even tough this work I'm not sure if this is considered a good practice.
=========================
ALTERNATIVE SOLUTION:
This also works fine and I guess it's a little bit more clean avoiding to create and requiring an extra module as the solution above. I've changed the 'export' in the Shim configuration using an initialization function. For further understanding see the Shim config reference.
//shim config in main.js file
shim: {
underscore: {
exports: '_',
init: function () {
this._.templateSettings = {
evaluate:/\{\{(.+?)\}\}/g,
interpolate:/\{\{=(.+?)\}\}/g,
escape:/\{\{-(.+?)\}\}/g
};
return _; //this is what will be actually exported!
}
}
}
You should pass your _ variable with template settings as function argument or as property in global object (window for browsers or proccess for nodejs).
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g
};
questionView = new QuestionView(_);
Or
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g
};
window._ = _
First option is better.
Bear in mind that if you're using underscore >=1.6.0 or lodash-amd the solution is pretty simple:
"main.js" configuration file
require.config({
baseUrl: './', // Your base URL
paths: {
// Path to a module you create that will require the underscore module.
// You cannot use the "underscore" name since underscore.js registers "underscore" as its module name.
// That's why I use "_".
_: 'underscore',
// Path to underscore module
underscore: '../../bower_components/underscore/underscore',
}
});
Your "_.js" file:
define(['underscore'], function(_) {
// Here you can manipulate/customize underscore.js to your taste.
// For example: I usually add the "variable" setting for templates
// here so that it's applied to all templates automatically.
// Add "variable" property so templates are able to render faster!
// #see http://underscorejs.org/#template
_.templateSettings.variable = 'data';
return _;
});
A module file. It requires our "_" module which requires "underscore" and patches it.
define(['_'], function(_){
// You can see the "variable" property is there
console.log(_.templateSettings);
});

Categories

Resources