Properly loading webshims / modernizr with requirejs? - backbone.js

I have modernizr / pollyfiller included at the top of my index.html file, and in my main.js:
require.config({
paths : {
'jquery' : 'lib/jquery-1.10.2.min',
'jqdate' : 'lib/jquery.dateFormat-1.0',
'webshims' : 'lib/polyfiller'
},
shim : {
'lib/underscore' : {
exports : '_'
},
'lib/backbone' : {
deps : ["lib/underscore", "jquery"],
exports : 'Backbone'
},
"modernizr" : {
deps : ["jquery"],
exports : "modernizr"
},
"webshims" : {
deps : [ "jquery", "lib/modernizr-custom"],
exports: "webshims"
}
}
});
var router, vent;
require(["jquery", "lib/underscore", "lib/backbone", "app", "lib/modernizr-custom", "webshims"], function($, _, Backbone, Router, modernizr, webshims) {
$(function() {
$.webshims.setOptions('forms forms-ext', {
replaceUI : false,
waitReady : false
});
$.webshims.polyfill('forms forms-ext');
router = new Router();
vent = _.extend({}, Backbone.Events);
$.expr.cacheLength = 1;
Backbone.history.start({
});
});
});
This will generally load fine, however, sometimes it looks like webshims is not defined by the time I try to call:
$.webshims.setOptions('forms forms-ext', {
replaceUI : false,
waitReady : false
});
$.webshims.polyfill('forms forms-ext');
and I get the error: TypeError: $.webshims is undefined
Is there a better way to load this?
edit
So, I updated the script like you said, and had to capitalize Webshims in the paths and shim definition. It loads fine but now I get an error:
Uncaught SyntaxError: Unexpected token <
in Chrome and
SyntaxError: syntax error
<!DOCTYPE html>
in firefox

Updated Answer
Alexander Farkas pointed out in a comment that polyfiller defines itself as "polyfiller" like this:
define('polyfiller', ['jquery'], factory);
So:
No shim should be required to load polyfiller.js.
The module defined by polyfiller.js should always be referred to as "polyfiller". So there has to be a paths setting that maps the module name polyfiller to the actual path of the polyfiller.js file.
So the original config should be modified to remove the "webshims" shim, then the paths setting "webshims": "lib/polyfiller" should become "polyfiller": "lib/polyfiller" and the require call should be:
require(["jquery", "lib/underscore", "lib/backbone", "app", "lib/modernizr-custom", "polyfiller"], function($, _, Backbone, Router, modernizr) {
I've dropped the last variable from the function's parameters because there's no need to pass the module value since the polyfiller.js file registers itself as $.webshims.
This is similar to how jQuery defines itself as "jquery" (it needs no shim and is always called "jquery").
Original Answer
Change your require call so that you require "webshims" instead of "lib/polyfiller":
require(["jquery", "lib/underscore", "lib/backbone", "app", "lib/modernizr-custom", "webshims"], ...
The code in your question shows you've set the paths configuration option so that the module name "webshims" resolves to "lib/polyfiller", and created what looks like a sensible shim for it. However, when you require the webshims module you refer to it as "lib/polyfiller". RequireJS does not do a reverse resolution to figure out that "lib/polyfiller" is "webshims".
Alternatively, you could drop the "webshims" name from paths and rename the shim so that it is set for "lib/polyfiller". However, I consider it to be a better practice to refer to 3rd party libraries by one-word names throughout an application rather than have paths for them. So "jquery", "bootstrap", "underscore", and "webshims" etc. rather than "lib/...".

Related

WHat would be a proper way to require modules

Consider the following requirejs config:
requirejs.config({
waitSeconds: 10,
baseUrl: 'js/libs',
paths: {
'jQuery' : 'jquery',
'jQueryUi' : 'jquery.ui',
'cookie' : 'jquery.cookie',
'underscore' : 'underscore',
'backbone' : 'backbone',
'text' : 'text',
'reusable' : '../../tmpl/reusable.tmpl.html'
},
shim: {
'jQuery':{
exports: '$'
},
'cookie':['jQuery'],
'jQueryUi':['jQuery'],
'underscore': {
exports: '_'
},
'backbone':{
deps: ['underscore'],
exports: 'Backbone'
}
}
});
require(['cookie','jQueryUi'],function(){
require(['backbone'],function(){
this._templates = {};
require(['text!reusable'],function(reusable){
this._templates['reusable'] = reusable;
});
});
});
Everything works as expected so far...
Contents of some_module.js:
define(function(){
var init = function(){
console.log('some_module initialized');
};
return{
init:init
};
});
Based on the Backbone router navigate function, some_module is required at a certain point like so:
require(['some_module'],function(module){
module.init();
});
On route change, a similar module is called the same way.
When navigating, the user may return to the previous route, meaning that some_module is requested again, this time from the cache since some_module.js was stored there on the first call.
Question:
Since some_module can be requested once, or multiple times, is it a good practice to load the module every time, even if the file is cached ?... or initially set it in the global scope:
var some_module = require('some_module');
and each time the module is needed, just call:
some_module.init()
or whatever property it may return?
What would be the difference when it comes to memory leaks, and general application functionality?
The general rule is that everything you load through RequireJS is loaded once and only once.
The first time the module is needed (due to require or a define that has the module as a dependency), it will be actually fetched from the network and then its factory function will be executed. (The factory is the function that you give to define.) The result of the factory function is cached by RequireJS.
When the module is needed again, the module is returned from RequireJS cache without using the browser's cache of files or going to the network.

Chrome extension using RequireJS, Backbone (Chaplin) conflicts

I am creating a Google Chrome Extension that have to add content on the visited websites (like a toolbox).
I have to use RequireJS and BackboneJS (Chaplin) and everything is ok except when i'm visiting a website using RequireJS (and Backbone, but the problem seems to come from RequireJS conflicts).
(This is when I use content scripts to include a -script- tag that includes RequireJS.)
I suppose it's normal to have conflicts if I add content directly in the page so I tried the solution here : Loading multiple instances of requireJS and Backbone
It seems to work (for now), but the website is trying to reload his own RequireJS file (with his path, but in my extension) before loading mine and I'm afraid it could lead to unexpected behaviour.
Plus, I have to precise my file paths in requirejs.config or it's looking for them in Bitbucket sources (cloudfront). (Maybe it's normal though)
Example with bitbucket :
Denying load of chrome-extension://mgncmiffelpdhlbkkmmaedbodabdchea/https://d3oaxc4q5k2d6q.cloudfront.net/m/7aaf1677069c/amd/build/main.js?8uxr. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
<--------- This file is Bitbucket's RequireJS, Bitbucket is still working fine though
Is there another solution I didn't find yet ? Or am I doing it wrong ? I'm a beginner with RequireJS (and Chrome ext.. and Backbone...) so I might have missed something.
Here is the Content script part in manifest.json
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"],
"js": ["bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"]
}],
init-app.js is Rob's script
require.load = function(context, moduleName, url) {
url = chrome.extension.getURL(url);
var x = new XMLHttpRequest();
// Append Math.random()... to bust the cache
x.open('GET', url + '?' + Math.random().toString(36).slice(-4));
x.onload = function() {
var code = x.responseText;
x += '\n//# sourceURL=' + url; // Optional, for debugging.
window.eval(code);
context.completeLoad(moduleName);
};
x.onerror = function() {
// Log error if you wish. This is usually not needed, because
// Chrome's developer tools does already log "404 Not found"
// errors for scripts to the console.
};
x.send();
};
and main.js contain requirejs.config + app
// Configure the AMD module loader
requirejs.config({
skipDataMain: true,
// The path where your JavaScripts are located
baseUrl: 'extension',
// Specify the paths of vendor libraries
paths: {
jquery: '../bower_components/jquery/jquery',
underscore: '../bower_components/lodash/dist/lodash',
backbone: '../bower_components/backbone/backbone',
handlebars: '../bower_components/handlebars/handlebars',
text: '../bower_components/requirejs-text/text',
chaplin: '../bower_components/chaplin/chaplin',
application: '/extension/application',
routes: '/extension/routes',
},
// Underscore and Backbone are not AMD-capable per default,
// so we need to use the AMD wrapping of RequireJS
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
handlebars: {
exports: 'Handlebars'
}
}
// For easier development, disable browser caching
// Of course, this should be removed in a production environment
//, urlArgs: 'bust=' + (new Date()).getTime()
});
// Bootstrap the application
require(['application', 'routes'], function(Application, routes) {
new Application({routes: routes, controllerPath: 'scripts/controllers/', controllerSuffix: '-controller'});
});
It works on gooogle.com for instance, but I get
GET chrome-extension://ccgfmmmnebacpnbdpdnphmnmicaooddg/extension/Home.js?9zfr net::ERR_FILE_NOT_FOUND
on https://www.cloud9trader.com (website using RequireJS) because it has
<script data-main="/0.2.59/scripts/Home.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.14/require.min.js"></script>
in its source. To summarize I just need the script to ignore the "current" website Require file.
The skipDataMain option is synchronously checked when require.js is loaded. Setting this variable after loading require.js has no effect on the loader any more, because the data-main scan has already run at that point.
The correct way to skip data-main is to declare the configuration before loading require.js, as follows:
// extension/config.js
var require = {
skipDataMain: true
};
manifest.json:
{
...
"content_scripts": [{
"matches": ["https://*/*", "http://*/*"],
"js": [
"extension/config.js",
"bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"
]
}],
...
}

requirejs + backbonejs + optimizer = backbone undefined

Backbone doesn't get called in the indexview module.
using requirejs 2.1.5/2.1.4 and backbonejs 0.9.10
main.js after running r.js
...
// this is causing the backbone to return
// null/undefined in the next define call below
define("backbone", function(){});
define('views/index/IndexView', [
'underscore',
'backbone',
'text!templates/index/indexTemplate.html'
], function(_, Backbone, indexTemplate){
console.log(Backbone); // returns undefined
var IndexView = Backbone.View.extend({
...
BUT if I take out the first define call that registers backbone as a module, everything
works fine. but backbone-min.js gets loaded separately. but for now it's the only way
to make the script run. I am definitely missing something here.
main.js
require.config({
paths: {
underscore : 'libs/underscore/underscore-min',
backbone : 'libs/backbone/backbone-min'
templates : '../templates'
},
shim: {
'backbone': {
deps: ['jquery','underscore'],
exports: 'Backbone'
}
}
});
require(['app'], function(App){
App.initialize();
});
build.js
({
appDir: "../",
baseUrl: "js",
dir: "../../build",
optimize: "none",
paths: {
"jquery": "libs/requirejs/require-jquery",
"underscore" : 'libs/underscore/underscore-min',
"backbone": 'libs/backbone/backbone-min',
"templates": '../templates',
},
modules: [
{
name: "main",
exclude: ["jquery"]
}
]
})
I'm still getting my feet wet with backbone and requirejs.
any feedback is much appreciated.
First of all, you don't need that define('backbone',...). What is that part for anyways? You don't need to define Backbone as a module. Requirejs is doing the job of making Backbone available for you to use throughout your framework. As you can see in your code, by calling Backbone.View.extend(), Backbone already exists. If you want to inspect it, don't use console.log, use console.dir instead. In the chrome inspector it formats the output nicely.
Secondly, add underscore to the backbone deps array in your shim.
I've just had this issue, you need to add the 'shim' from your main.js to your build.js file, and will work like a charm ;)

backbone.marionette with handlebars throwing exceptions trying to load my template

here is the error thrown to my console
Uncaught TypeError: Object has no method 'chain' hbs.js:282
getExternalDeps hbs.js:282
(anonymous function) hbs.js:306
xhr.onreadystatechange hbs.js:73
Uncaught Error: Load timeout for modules: hbs!templates/reset_unnormalized2,hbs!templates/pages/login_unnormalized3,hbs!templates/reset,hbs!templates/pages/login
http://requirejs.org/docs/errors.html#timeout require.js:160
i'm using the handlebars-require-plugin and i've even tried adding "hbs" to my require shim but the result is the same.
here is my marionette view code:
define([
"bootstrap", // bootstrap does not export anything, so no arg sent to our function
"marionette",
"session", "events",
"hbs!templates/pages/login"
], function( Marionette, Session, Events, LoginTemplate ){
return Marionette.View.extend({
template : {
type : "handlebars",
template : LoginTemplate
}
});
});
i have no idea what's going on here and every search i've pulled up has yielded no useful results.
what's happening? why is this happening? please help.
Could be that you have your args to your callback incorrectly defined? If a value does not define anything (such as bootstrap) then make it last. Right now your Marionette arg contains the undefined value of bootstrap, Session now holds Marionette etc.
define([
"marionette",
"session", "events",
"hbs!templates/pages/login",
"bootstrap", // bootstrap does not export anything, so no arg sent to our function
], function( Marionette, Session, Events, LoginTemplate ){
return Marionette.View.extend({
template : {
type : "handlebars",
template : LoginTemplate
}
});
});
i figured it out.
it turns out my problem was not related to the hbs plugin as it was with using it, handlebars, and json2 with requirejs.
the first problem, as described above, was solved by switching out the lodash library with underscore. i was under the impression that these two were interchangeable, but apparently that is NOT the case.
then i was getting errors that "stringify" is not a function on the object null which led me to find that JSON was not defined inside hbs.
i had to go back to my require.config({ shim:{} }) and define json2 with an export of JSON
now i am not getting errors from hbs but i have to figure out why my view is not displaying.
here is my config code, should anyone else be interested.
require.config({
// paths to required javascript files
paths : {
bootstrap : "../assets/js/bootstrap.min",
jquery : "../assets/js/jquery.min",
json2 : "../assets/js/json2",
underscore : "../assets/js/underscore.min",
backbone : "../assets/js/backbone.min",
marionette : "../assets/js/backbone.marionette.min",
hbs : "../assets/js/hbs",
handlebars : "../assets/js/handlebars",
i18nprecompile : "../assets/js/hbs/i18nprecompile"
},
// "shim" will allows us to load in deps synchronously so that we do not
// run into a situation where dependencies are not available
shim : {
json2 : { exports : "JSON" },
jquery : { exports : "jQuery" },
underscore : { exports : "_" },
backbone : {
deps : ["jquery", "underscore", "json2"],
exports : "Backbone"
},
marionette : {
deps : ["jquery", "underscore", "json2", "backbone"],
exports : "Marionette"
},
hbs : {
deps : ["jquery", "underscore", "json2", "i18nprecompile", "handlebars"],
exports : "hbs"
},
bootstrap : { deps : ["jquery"] }
}
});

Requirejs2: How to handle own files?

I have configured requirejs to load the core libs (jquery, underscore, backbone).
Now I would like to add my backbone models, controllers, views, etc to be loaded asyncronly
I found a lots of tutorials to this topic and lots of "ready" boilerplates unfortunatly I mentioned that most approaches are depreceated or rather complicated (even there are better approaches).
One example is how I configured requirejs for the main libs:
https://stackoverflow.com/a/10914666/1309847
So how do I load Backbone Views, Models, Collections, Routers, Controllers and Templates with a simple and valid Requirejs configuration?
I followed youre advice but get some strange error
main.js
require.config({
paths: {
jquery: 'vendors/jquery/jquery',
underscore: 'vendors/underscore/underscore',
backbone: 'vendors/backbone/backbone'
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require(['app'], function(app){
});
app.js
define(['jquery', 'underscore', 'backbone'], function($, _, Backbone){
var Message = new Backbone.Model.extend({
//idAttribute: '_id',
//defaults: { body: '' }
//url: function(){ return this.id ? '/messages/' + this.id : '/messages'; }
});
var newMessage = new Message({ body: 'hi' });
newMessage.save();
});
The error occours in app.js:
Uncaught TypeError: Object [object Object] has no method 'apply'
When I comment the new Backbone.Model.extend part I don't get any error anymore.
in my experience, the best way to bootstrap your application is by creating a Backbone.Router. So you can associate urls with your application functionality.
If you are using RequireJS+Backbone, you probably have a main.js where RequireJS is configured (paths, shims, etc). The first call to "require" is used to load a initial script in order to bootstrap the whole app.
For example:
/**
* main.js - RequireJS bootstrap
*/
require.config({
paths: {
//your paths
},
shim: {
//your shims
}
});
require(
[
'app' //app.js is at the same directory as main.js
],
function(app) {
app.init();
}
);
then in app.js you can create a new Router instance, or you can just start creating Views and Models.
For further reference: http://addyosmani.github.com/backbone-fundamentals/
So as I have now understood right: You have to wrap a requirejs function around youre own custom js file.
The function is called define. The first parameter is an array of the dependencies which you have defined in the main.js file or a relative path to another custom js from you.
The second parameter is the callback which holds the original file. Important is that you return the object, function, array or variable which you want to share.
The whole thing looks like this:
define(
['underscore', 'backbone'], // the dependencies (either relative paths or shortcuts defined in main.js
function(_, Backbone){ // the return statement of the deps mapped to a var
var MessageModel = Backbone.Model.extend({ // the original code, file
defaults: { body: '' },
initialize: function(){}
});
return MessageModel; // the return statement, sharing the "final result", sometimes you return the initialize parameter
});
The same for a collection wrapping the models:
define(
['jquery', 'underscore', 'backbone', 'models/message_model'], // deps and the last one is the relative path
function($, _, Backbone,MessageModel){ // same as above explained
var MessageCollection = Backbone.Collection.extend({
model: MessageModel,
initialize: function(){}
});
return MessageCollection;
});
I now only have to figure out how I can bootstrap to whole application. But I think I need more knowledge of backbone to do this :)

Resources