How to use the domReady requireJS plugin correctly - javascript

Here is my main.js before using domReady:
require.config({
paths : {
loader : 'libs/backbone/loader',
jQuery : 'libs/jquery/jquery-module',
Underscore : 'libs/underscore/underscore-module',
Backbone : 'libs/backbone/backbone-module',
templates : '../Templates'
}
});
require([ 'app' ], function(app) {
app.initialize();
});
And app.js:
define([ 'jQuery', 'Underscore', 'Backbone', 'router',
'services/Initializers/MainFrameInitializer',
'services/Initializers/FlowsViewsInitializer',
'services/Initializers/EditModuleInitializer',
'services/Sandboxes/ModulesNavigationSandbox',
'services/Sandboxes/ApplicationStateSandbox', 'DataModel/Constants' ],
function($, _, Backbone, Router, MainFrameInitializer,
FlowsViewsInitializer, EditModuleInitializer, ModulesNavigationSandbox,
ApplicationStateSandbox, Constants) {
var initialize = function() {
// Pass in our Router module and call it's initialize function
MainFrameInitializer.initialize();
FlowsViewsInitializer.initialize();
EditModuleInitializer.initialize();
ApplicationStateSandbox.startCheckStatus();
ModulesNavigationSandbox.navigate(Constants.Modules.Home);
// Router.initialize();
};
return {
initialize : initialize
};
});
All works fine until I optimize the project. I have figured out, that the script starts to run before the DOM is ready, something that was not true before the optimization. Anyway, I wish to use the domReady plugin to make sure the DOM is loaded first.
But, apparently, I have no idea how to do it correctly. Here is the new version of main.js:
require.config({
paths : {
loader : 'libs/backbone/loader',
jQuery : 'libs/jquery/jquery-module',
Underscore : 'libs/underscore/underscore-module',
Backbone : 'libs/backbone/backbone-module',
templates : '../Templates'
}
});
require([ 'domReady', 'app' ], function(domReady, app) {
domReady(app.initialize);
});
Very neat and very wrong, because app is loaded in parallel with domReady before the DOM is ready.
How do I fix it?
Thanks.
EDIT
I think I have understood our problem. The constructor functions of the app dependencies should not run any DOM dependent code. They should just return functions, capturing the DOM dependent logic. That logic should be executed from app.initialize, which is guaranteed to be run when the DOM is ready.

Perhaps I am missing something, but wouldn't you make your life a lot easier by doing:
require(['jQuery', 'app' ], function(jQuery, app) {
jQuery(function ($) {
app.initialize();
});
});
in your main.js?

By requiring the app from inside the domReady callback function, you should be able to require the domReady module, and then the app module synchronously.
define(['require', 'domReady'], function(require, domReady) {
domReady(function() {
require(['app'], function(app) {
app.initialize();
});
});
});

If you follow the doc at: http://requirejs.org/docs/jquery.html, you will be invited to embed a require-jquery library in the head of your document:
<script data-main="main" src="libs/require-jquery.js"></script>
However, if you look at the source for the example, made available on github, you will see that 'require-jquery.js' is generated by simple concatenation of the require lib file and the jquery lib file:
cat require.js jquery.js > ../jquery-require-sample/webapp/scripts/require-jquery.js
That means that you could replace the script embed in the head part with the following script embeds anywhere in your document (for instance at the very bottom of it).
<script src="libs/require.js"></script>
<script src="libs/jquery-1.8.0.js"></script>
<script>require(["main"]);</script>
Because the jquery lib defines itself as a module with:
define( "jquery", [], function () { return jQuery; } );
You can thereafter use jquery as a require reference in any of your script. For instance:
require(["jquery"], function($) { }

Related

Require.js: load file from build but don't execute it

I'm working on an application that combines ember.js and jquery-mobile.js
In order to make those two play nice with each other, I need to load JQM after Ember is initialized. So I use the following code in my main file, app.js:
require.config({
baseUrl: 'resources/js/',
waitSeconds: 200,
paths: {
text: 'lib/require/text',
ember: 'lib/ember-1.5.1.min',
jquery: 'lib/jquery-2.1.1.min',
mobile: 'lib/jquery.mobile-1.4.2.min',
handlebars: 'lib/handlebars-1.3.0.min',
},
shim: {
'ember': {
deps: ['handlebars', 'text', 'jquery']
}
}
});
define('app', [
'jquery',
'app/many/files',
'ember'
], function($,
ManyFiles) {
$(document).bind('mobileinit', function() {
$.mobile.ajaxEnabled = false;
$.mobile.pushStateEnabled = false;
$.mobile.linkBindingEnabled = false;
$.mobile.hashListeningEnabled = false;
$.mobile.ignoreContentEnabled = true;
});
App = Ember.Application.create({
ready: function() {
require(['mobile']);
}
});
// Initialize stuff...
}
(As you can see, jqm is loaded only when the ember application is ready)
This works great, but when I build all the files into a single minified js file, I run into this problem: As soon as the code requires JQM, I see an http call on the network tab which goes to grab jquery-mobile.js
Of course this is an unpleasant process. The only solution I can think of is to load JQM along with all the other dependencies but not execute it. Then, the code can execute JQM instead of requiring the file.
However I am not experienced on require.js and I have no idea on how to do that. Any help is appreciated. Other methods to accomplish the same thing are also appreciated
Thanks
EDIT:
Why does JQM needs to get loaded after Ember?
Because JQM add wrappers, classes and events on the DOM that interfere with ember... and things get really bad
After a lot o fiddling I found out an answer to my problem:
...
App = Ember.Application.create({
ready: emberInit
});
// Initialize stuff
}
function emberInit() {
require(['mobile']);
}
It seems like the require statement was too nested for it to work normally (maybe a bug?)
NOTE:
While trying to find an answer to my problem I found out about a functionality of require.js that I would like to share with people that might have a similar problem and bump into this thread:
Apparently you can set a callback on the require configuration to run when all dependencies are loaded, like that:
require.config({
deps: ['jquery', 'handlebars', 'ember', 'socketio'],
callback: function () {
// Do stuff when above dependencies load
},
});
which was not EXACTLY what I needed, but others might find helpfull

Using knockout.simpleGrid.3.0.js with Require.js

I am using require.js with knockout on a website and would like to use the simpleGrid example from this link http://knockoutjs.com/examples/grid.html however I cannot include kncokout.simpleGrid.3.0.js with Require.
I have tried wrapping the plugin with
define(['jQuery', 'knockout'], // Require knockout
function($, ko) {
});
This does not work it seems the problem occurs with the templates.
Any help appreciated
In your require config, you should create a path to the simpleGrid library and use the shim to tell it that it depends on Knockout so that your libraries are loaded in the correct order. Here's an example:
var require = {
paths: {
'jquery': 'lib/vendor/jquery-2.0.3',
'ko': 'lib/vendor/knockout-3.0.0',
'koSimpleGrid': 'lib/vendor/knockout.simpleGrid.3.0'
},
shim: {
'koSimpleGrid': {
deps: ['ko']
},
}
};
And then you could copy paste the view model code from the example inside of a define like this:
define(['jquery', 'ko', 'koSimpleGrid'], function ($, ko) {
// VIEW MODEL GOES HERE
});
I agree the problem seems to be with the code that writes the grid template. Essentially, because requirejs loads modules asynchronously, document.write() can't be used - writing of the document has finished by the time a module executes. This StackOverflow answer explains it well I think.
I got round it by instead creating and appending the required script tag templates using dom methods:
templateEngine.addTemplate = function(templateName, templateMarkup) {
var scriptTag = document.createElement('script');
scriptTag.type = 'text/html';
scriptTag.id = templateName;
scriptTag.text = templateMarkup;
document.getElementsByTagName('body')[0].appendChild(scriptTag);
};
My amd version is in this gist.

Dojo AMD loader executing define callbacks with empty / missing dependencies

I'm new to Dojo (1.7) and totally willing to accept I'm being an idiot (I just hope not). I am more comfortable using require.js for AMD but I'm using a 3rd-party (ESRI) mapping API that forces Dojo on me and uses its AMD, meaning I get nasty errors if I try and use require.js as well.
I have defined a module with dependencies on Backbone and Underscore (I might ultimately go with Dojo's MVC but I don't think this problem is specific to Backbone so I want to get it figured out). Bizarrely it seems that Dojo is executing the callback within my define when the module is loaded, and at this point the dependencies (Underscore and Backbone) are empty objects {}. An error occurs within my callback's return Backbone.View.extend... because Backbone's View property doesn't exist.
I know that Backbone is dependent on Underscore, and so far I have no idea how to ensure Underscore is loaded first without using a hacky-looking require({async:0},['test1.js','test2.js'.... However, in this case Underscore is also an empty object, so the define's callback is executed before either dependency is loaded???
EDIT I see both Underscore and Backbone HTTP requests and 200 responses in the console before this error occurs, so I assume there are no problems in their references.
Something concrete...
index.html:
<script type="text/javascript">
var dojoConfig = {
tlmSiblingOfDojo: false,
packages: [
{name: 'app', location: '/js'},
{name: 'lib', location: '/js/lib'}
],
aliases: [
['Backbone', 'lib/backbone-0.9.2.min'],
['_', 'lib/underscore-1.3.3.min'],
['$', 'lib/jquery-1.8.0.min'],
['ready', 'dojo/domReady']
]
};
</script>
<script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=3.1"></script>
<script type="text/javascript">
require([ 'app/app' ], function(App) {
App.initialize();
});
</script>
app.js:
define(['app/views/main-view'], function(MainView) {
return {
initialize : function() {
new MainView();
}
};
});
main-view.js:
define(['_', 'Backbone', 'ready!'], function(_, Backbone) {
// *** ERROR THROWN HERE, Backbone = {}, _ = {} ***
return Backbone.View.extend({
el: 'main',
initialise: function() {
console.log('main view initialising');
this.render();
},
render: function() {
console.log('main view rendering');
}
});
});
Can anybody (please) tell me what's going on here? Also any alternate suggestions on loading Underscore before Backbone would be really helpful!
Just change the order of dependencies:
define(['_', 'Backbone', 'ready!'], function(_, Backbone) { /*...*/});
Because your have ready! plugin in _ variable and _.js in Backbone variable.
Edit: You can nest it:
define(["_", "require"], function(_, require) {
require(["Backbone"], function(Backbone) {
// your code here
})
})
Also, if underscore or Backbone are not AMD modules, local function variables will cover them.

Cannot get requirejs to include Backbonejs

I am trying to modularize my Backbone app through Requirejs, but I cannot seem to get Requirejs to include Backbone. Here is my main.js that gets included from the index page:
require.config({
baseUrl: '/static/js/',
paths: {
jquery: 'libs/jquery/jquery-min',
underscore: 'libs/underscore/underscore-min',
backbone: 'libs/backbone/backbone-min',
text: 'libs/require/text',
},
});
require(['/static/js/views/app.js'], function(AppView) {
var appView = new AppView;
});
Here is my app.js that gets included from the above file:
define([
'jquery',
'underscore',
'backbone',
], function($, _, Backbone) {
console.log($);
console.log(_);
console.log("inside of the app js file");
var AppView = Backbone.View.extend({
initialize: function() {
console.log("inside of appview initialize");
},
});
});
The following information gets printed out in my Google Chrome console:
function (a,b){return new e.fn.init(a,b,h)}
app.js:7undefined
app.js:8inside of the app js file
app.js:9Uncaught TypeError: Cannot read property 'View' of undefined
The definition for $ works, but the definitions for _ and Backbone do not work. They come up as undefined. Any idea why this is happening?
I recommend you use the version of requireJS that has jQuery bundled. It will save you much setup pain. You can read the details here: http://requirejs.org/docs/jquery.html and download the files here: https://github.com/jrburke/require-jquery
In their own words:
With RequireJS, scripts can load in a different order than the order they are specified. This can cause problems for jQuery plugins that assume jQuery is already loaded. Using the combined RequireJS + jQUery file makes sure jQuery is in the page before any jQuery plugins load.
This should take care of loading jQuery properly with requireJS:
<script data-main="js/main" src="js/require-jquery.js"></script>
Main.js
A couple of notes here:
No need to re-define the path to jquery since that's already taken care of
You still have to indicate jquery as a required module
(I had to update the paths to have them work in my system)
Code:
require.config({
baseUrl: 'js/',
paths: {
underscore: 'libs/underscore-min',
backbone: 'libs/backbone-min',
},
});
require(['jquery', 'app'], function($, AppView) {
console.log("done w/ requires");
console.log($)
console.log(AppView)
var appView = new AppView;
});
app.js
A couple notes:
You can only retrieve the JS files after loading them in the callback if they have been defined as modules. So function($, _, Backbone) will fail for you.
You must return your object so that it can be used in the main.js callback (return AppView)
Code:
define(
[
'jquery',
'underscore',
'backbone',
],
function() {
console.log($);
console.log(_);
console.log(Backbone);
console.log("inside of the app js file");
return AppView = Backbone.View.extend({
initialize: function() {
console.log("inside of appview initialize");
},
});
});
Console
With that code in place, this is the console output:
function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
} [app.js:8]
function (a){return new m(a)} [app.js:9]
Object [app.js:10]
inside of the app js file [app.js:11]
done w/ requires main.js:21
function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
} [main.js:22]
function (){a.apply(this,arguments)} [main.js:23]
inside of appview initialize [app.js:15]
I would shy away from using forked versions of backbone and underscore. A "shim" configuration option has been added to requirejs to handle 3rd party libraries that don't natively support AMD. This makes updating to the latest versions of the libraries much easier.
http://requirejs.org/docs/api.html#config-shim
Here is an example:
require.config({
paths: {
jquery: "libs/jquery",
underscore: "libs/underscore",
backbone: "libs/backbone"
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ["underscore", "jquery"],
exports: "Backbone"
}
}
});
You may not have the correct reference backbone and _,may be this can help you:
Loading Backbone and Underscore using RequireJS
more:
http://requirejs.org/docs/api.html#config-shim

Unable to Create Backbone View

I'm continually getting index.js:7 Uncaught TypeError: Cannot read property 'View' of null, which indicates that Backbone is not loaded/present, however, when I review the loaded resources on the page backbone-min.js is present.
Since there are no 404 errors, I believe the issue is with the script itself. Does anyone see any issues with the script below?
NOTE: For convenience I uploaded my code here. The zip file contains all the relevant js files. If you scroll to the bottom of the webpage, you should see a "slow
download" button, once you click it you'll be prompted to enter a
captcha code. After entering the code, the actual download button
(under the "slow download" button) will appear within a few seconds.
View: index.js
define([
"jQuery",
"Underscore",
"Backbone"
// I've tried using the modules above as well as direct loading using order! as seen in the following lines.
//"order!libs/jquery/jquery-min",
//"order!libs/underscore/underscore-min",
//"order!libs/backbone/backbone-min",
],
function($, _, Backbone){
console.log(_) // prints "undefined"
console.log(Backbone) // prints Object
var IndexView = Backbone.View.extend({ // At this line I now get: Uncaught TypeError: Cannot call method 'extend' of undefined
render: function(){
$(this.el).html("<h1>Welcome Dan!</h1>");
$("body").html(this.el);
}
});
return new IndexView();
});
So the key to this issue is changes in underscore.js. Specifically the fact it now supports AMD (Asynchronous Module Definition). The fact that underscore no longer attaches itself to the global namespace when require is detected is breaking the scheme used to allow a standard asynchronous require syntax but still maintain synchronous loading.
Now that JQuery, Underscore & Backbone (0.5.3 does not register itself, you need a this) support async loading, you are able to abandon those hacked libraries in favor of the standard ones and require the names those libraries register themselves with. Like this:
Main.js
require.config({
baseUrl: "js",
paths: {
jquery: "libs/jquery/jquery",
underscore: "libs/underscore/underscore",
backbone: "libs/backbone/backbone"
},
waitSeconds: 10
});
require([
"app"
],
function(App){
App.initialize();
console.log("Main initialized...");
});
index.js
define([
"jquery",
"underscore",
"backbone"
],
function($, _, Backbone){
console.log(_);
console.log(Backbone);
var IndexView = Backbone.View.extend({
render: function(){
var username = getCookie("username");
var data = {username: username};
var compiled = _.template("<h1>Welcome <%= username %></h1>", data);
$(this.el).html(compiled);
$("#lt-col").html(this.el);
}
});
return new IndexView();
});
Other define's were changed to reflect the new lowercase alias's.
Pull the fixed code here
Even though Backbone 0.5.3 registers itself as an AMD module, it doesn't return anything. (Same thing for underscore) If you change your line:
function($, _, Backbone){
To
function($){
It will work. For a more requirejs-ish solution, create a module for backbone that looks like:
define(
[
'order!libraries/underscore',
'order!libraries/backbone.0.5.3'
],
function () {
return Backbone;
}
);
--UPDATE-- additional info
<head>
<title>Index2</title>
<script src="../../scripts/libraries/require.js" type="text/javascript"></script>
<script type="text/javascript"">
require({
baseUrl: 'scripts'
}, [
'order!libraries/jquery',
'order!libraries/underscore',
'order!libraries/backbone.0.5.3'
], function ($) {
console.log(Backbone);
});
</script>
</head>

Categories

Resources