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)?
Related
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
Problem
When executing the compiled handlebars templates the global Handlebars object isn't exported. NOTE: the global Backbone object is working.
See, when the code App.templates.todos attempts to execute in the todos.js file it fails because App.templates.todos isn't defined. Well ultimately that's because the third line in the templates.js file can't execute because the global Handlebars object isn't defined.
Why wouldn't that object get defined? What did I do wrong with require.js here?
UPDATE: I've verified that the handlebars.runtime.js file is in fact executing before the templates.js file and so require.js is running them in the right order when loading the todos.js file.
Bower Components
{
"name": "todomvc-backbone-requirejs",
"version": "0.0.0",
"dependencies": {
"backbone": "~1.1.0",
"underscore": "~1.5.0",
"jquery": "~2.0.0",
"todomvc-common": "~0.3.0",
"backbone.localStorage": "~1.1.0",
"requirejs": "~2.1.5",
"requirejs-text": "~2.0.5",
"handlebars": "~2.0.0"
},
"resolutions": {
"backbone": "~1.1.0"
}
}
main.js
/*global require*/
'use strict';
// Require.js allows us to configure shortcut alias
require.config({
// The shim config allows us to configure dependencies for
// scripts that do not call define() to register a module
shim: {
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
backboneLocalstorage: {
deps: ['backbone'],
exports: 'Store'
},
handlebars: {
exports: 'Handlebars'
},
templates: {
deps: ['handlebars'],
exports: 'App'
},
underscore: {
exports: '_'
}
},
paths: {
jquery: '../bower_components/jquery/jquery',
underscore: '../bower_components/underscore/underscore',
backbone: '../bower_components/backbone/backbone',
backboneLocalstorage: '../bower_components/backbone.localStorage/backbone.localStorage',
handlebars: '../bower_components/handlebars/handlebars.runtime',
templates: '../../../templates',
text: '../bower_components/requirejs-text/text'
}
});
require([
'backbone',
'views/app',
'routers/router'
], function (Backbone, AppView, Workspace) {
/*jshint nonew:false*/
// Initialize routing and start Backbone.history()
new Workspace();
Backbone.history.start();
// Initialize the application view
new AppView();
});
todos.js
/*global define*/
define([
'jquery',
'backbone',
'handlebars',
'templates',
'common'
], function ($, Backbone, Handlebars, Templates, Common) {
'use strict';
var TodoView = Backbone.View.extend({
tagName: 'li',
template: App.templates.todos,
...
});
return TodoView;
});
templates.js
this["App"] = this["App"] || {};
this["App"]["templates"] = this["App"]["templates"] || {};
this["App"]["templates"]["stats"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
From official documentation of RequireJS:
The shim config only sets up code relationships. To load modules that
are part of or use shim config, a normal require/define call is
needed. Setting shim by itself does not trigger code to load.
So first you need to somehow call the Handlebars and after try to use it in templates.js.
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!
I'm new to require js and i'm using backbone with layout manager. I've pasted my code below and i'm not able to get the layout manager to work and i get this error :
"Uncaught TypeError: Cannot call method 'extend' of undefined"
in line where layoutmanager is used (Backbone.LayoutView.extend)
Main.js
require.config({
paths: {
jquery: 'lib/jquery-1.9.1.min',
underscore: 'lib/underscore-min',
backbone: 'lib/backbone-min',
handlebars: 'lib/handlebars',
layoutManager : 'lib/backbone.layoutmanager'
mousewheel : 'lib/jquery.mousewheel.min'
},
shim : {
'backbone' : {
deps: ['jquery', 'underscore' ],
exports: 'Backbone'
},
'layoutManager' : {
deps: ['jquery', 'underscore', 'backbone'],
exports: 'LayoutManager'
},
'mousewheel' : ['jquery']
}
});
require(["jquery", "underscore", "backbone", "layoutManager", "handlebars","mousewheel", "common"],function($, _, Backbone, LayoutManager, Handlebars,mousewheel, App) {
App.Views.HelloView = Backbone.LayoutView.extend({
template : '#hello_tmpl1',
});
App.Layouts.AppLayout = new BackBone.Layout({
template : '#layout',
views : {
".helloView" : new App.Views.HelloView()
}
});
$('body').empty().append(App.Layouts.AppLayout.el.render());
});
Common.js
define(function(){
var App = null;
App = { Models:{}, Views:{}, Collections:{}, Templates:{}, Router:{}, Layouts:{}};
return App;
});
function($, _, Backbone, LayoutManager
Here you name it LayoutManager.
App.Views.HelloView = Backbone.LayoutView.extend({
Here you try to use it as Backbone.LayoutView... which doesn't exist (thus the undefined). Try
App.Views.HelloView = LayoutView.extend({
The Backbone var you have here is different from the global one, think requirejs :)
New version of layoutmanager doesn't use 'LayoutView' to create views. Use Backbone.Layout.extend({ ...}) to create your views ... have fun .. ;)
What is the right way to execute code on Underscore when it gets loaded? I am trying to execute the below code to extend the _ exported namespace automatically when modules require it:
_.mixin(_.str.exports());
The docs are a bit vague but I think I put it in the shim init section? I tried the below but I can't even get a breakpoint to hit in the init:
require.config({
paths: {
jquery: 'libs/jquery/jquery.min',
underscore: 'libs/underscore/lodash.min',
underscorestring: 'libs/underscore/underscore.string.min'
},
shim: {
underscore: {
exports: '_'
}
underscorestring: {
deps: ['underscore'],
init: function (_) {
//Mixin plugin to namespace
_.mixin(_.str.exports());
return _;
}
}
}
});
When I try to do this and use underscorestring, I get this error:
Uncaught TypeError: Object function s(e){return new o(e)} has no
method 'startsWith'
Docs:
http://requirejs.org/docs/api.html#config-shim
http://requirejs.org/docs/api.html#config-callback
I don't know if it's the correct way, but I got it working by inverting things so that underscore depends on underscore.string. Also, this way you don't have to require underscore.string.
require.config({
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
deps: ['underscore.string'],
exports: '_',
init: function(UnderscoreString) {
_.mixin(UnderscoreString);
}
}
},
paths: {
'backbone' : 'lib/backbone',
'jquery' : 'lib/jquery/jquery',
'text' : 'lib/require/text',
'underscore' : 'lib/underscore',
'underscore.string' : 'lib/underscore.string'
}
});
.
Update: 3/14/2014
Underscore.js v1.6.0 brought back AMD compatibility and init() has been removed from RequireJS, so some refactoring is in order. To continue getting Underscore preloaded with Underscore.string I made a mixer module to pull them together.
New Require.js config
requirejs.config({
paths: {
'backbone' : '../lib/backbone/backbone',
'backbone.base' : '../lib/backbone/backbone.base',
'backbone.extensions' : '../lib/backbone/backbone.extensions',
'jquery' : '../lib/jquery/jquery',
'text' : '../lib/require/text',
'underscore' : '../lib/underscore/underscore',
'underscore.mixed' : '../lib/underscore/underscore.mixed',
'underscore.string' : '../lib/underscore/underscore.string'
},
shim: {
'backbone.base': {
deps: ['underscore.mixed', 'jquery'],
exports: 'Backbone'
},
}
});
underscore.mixed
define([
'underscore',
'underscore.string'
], function(_, _s) {
_.mixin(_s.exports());
return _;
});
The final step is to replace all instances of 'underscore' with 'underscore.mixed' in module definitions. I attempted moving Underscore into a file named underscore.base.js and making the regular underscore the mixer (like the Backbone setup) to avoid this step. Underscore, being a named module, disagreed with the plan.
Do you require underscorestring somewhere? Because if it isn't required, it won't be loaded.
I managed to get it working with almost exactly the same code you posted:
require.config({
paths: {
underscore: [
'//raw.github.com/documentcloud/underscore/master/underscore-min'
, 'lib/underscore'
]
, underscorestring: 'https://raw.github.com/epeli/underscore.string/master/dist/underscore.string.min'
}
, shim: {
underscore: { exports: '_' },
underscorestring: {
deps: ['underscore'],
init: function(_) {
_.mixin(_.str.exports());
return _; // guess, this is not needed.
}
}
}
, exclude: ['underscore']
});
require(['underscore', 'underscorestring'], function(_) {
console.log( _.chars("i'm a happy string.") );
});
Battling with this for hours before i understand what i was doing wrong
This is what i did wrong
You should not rename the file underscore.string in main.js
even though in my library i did rename the file in paths i name it back to 'underscore.string'
This is how your main.js should look like
require.config({
paths: {
underscore: 'lib/underscore',
'underscore.string' : 'lib/_string' ,
},
shim: {
underscore: {
exports: '_',
deps: [ 'jquery', 'jqueryui' ]
},
'underscore.string': {
deps: [ 'underscore' ]
},
}
....
You could then either add it as dependency with in your shim like i did for my mixin file
shim: {
mixin : {
deps: [ 'jquery', 'underscore', 'underscore.string' , 'bootstrap' ]
},
Or just define it in your different pages like
/*global define */
define([
'underscore.string'
], function ( ) {
it just work now you can access it through _.str or _.string
This is why you should do it this way and not try to name it something else
on line 663 of underscore.string.js
// Register as a named module with AMD.
if (typeof define === 'function' && define.amd)
define('underscore.string', [], function(){ return _s; });
Which means that it will only register it with AMD require JS if you are defining 'underscore.string'
For Mix in you could just with define
/*global define */
define([
'underscore',
'underscore.string'
], function ( ) {
_.mixin(_.str.exports());