Unable to resolve _ in a Backbone & RequireJS app - javascript

I'm relatively new to Backbone and RequireJS, so please bear with me. I'm getting an error when I do the following in my collection: _.range(0,10). It's giving me this error:
Uncaught TypeError: Cannot call method 'range' of undefined
Somehow the "_" is not getting resolved when my Collection is loaded. Here's my collection below:
define([
'jquery',
'underscore',
'backbone',
'collections/feed',
'text!/static/templates/shared/display_item.html'
], function($, _, Backbone, FeedCollection, DisplayItem){
debugger; // Added this to test the value of _
var FeedView = Backbone.View.extend({
el: '#below-nav',
initialize: function () {
this.feedCollection = new FeedCollection();
},
feed_row: '<div class="feed-row row">',
feed_span8: '<div class="feed-column-wide span8">',
feed_span4: '<div class="feed-column span4">',
render: function () {
this.loadResults();
},
loadResults: function () {
var that = this;
// we are starting a new load of results so set isLoading to true
this.isLoading = true;
this.feedCollection.fetch({
success: function (articles) {
var display_items = [];
// This line below is the problem...._ is undefined
var index_list = _.range(0, articles.length, 3);
_.each(articles, function(article, index, list) {
if(_.contain(index_list, index)) {
var $feed_row = $(that.feed_row),
$feed_span8 = $(that.feed_span8),
$feed_span4 = $(that.feed_span4);
$feed_span8.append(_.template(DisplayItem, {article: articles[index]}));
$feed_span4.append(_.template(DisplayItem, {article: articles[index+1]}));
$feed_span4.append(_.template(DisplayItem, {article: articles[index+2]}));
$feed_row.append($feed_span8, $feed_span4);
$(that.el).append($feed_row);
}
});
}
});
}
});
return FeedView;
});
I added the debugger line so that I could test the values of all the arguments. Everything loaded fine, except for _. Could this be something wrong with my config.js file?
require.config({
// Set base url for paths to reference
baseUrl: 'static/js',
// Initialize the application with the main application file.
deps: ['main'],
paths: {
jquery: 'libs/jquery/jquery.min',
require: 'libs/require/require.min',
bootstrap: 'libs/bootstrap/bootstrap.min',
text: 'libs/plugins/text',
underscore: 'libs/underscore/underscore',
backbone: 'libs/backbone/backbone',
json: 'libs/json/json2',
base: 'libs/base/base'
},
shim: {
'backbone': {
// These script dependencies should be loaded first before loading
// backbone
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'bootstrap': {
deps: ['jquery'],
exports: 'Bootstrap'
}
}
})
Your help is greatly appreciated. My head is spinning as a result of this error.

Based off the project that I'm working on, you need a shim for underscore as well. Underscore isn't 'exported' per say, so use this instead:
shim: {
'backbone': {
// These script dependencies should be loaded first before loading
// backbone
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'bootstrap': {
deps: ['jquery'],
exports: 'Bootstrap'
},
'underscore': {
exports: '_'
}
}
Seems this might also be 'duplicate' question of Loading Backbone and Underscore using RequireJS - one or two of the answers down the list there is a mention of this setup.

Related

Unable to get Masonry working with RequireJS

I'm trying to get Masonry loaded into my app using RequireJS, but it keeps causing backbone to spit out a "object is not a function" error anytime I add it.
Edit: possibly related to this issue.
main.js
require.config({
paths: {
jquery: 'lib/jquery-1.9.1',
underscore: 'lib/underscore-1.5.2',
backbone: 'lib/backbone-1.0.0',
masonry: 'lib/masonry.pkgd'
},
shim: {
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
underscore: {
exports: '_'
}
}
});
require(['app'], function(App){
App.initialize();
});
app.js
define([
'jquery',
'underscore',
'backbone',
'masonry',
'collections/ideas',
], function($, _, Backbone, Masonry, IdeasCollection) {
var IdeasView = Backbone.View.extend({
el: $('#container'),
initialize: function() {
...
},
render: function(){
...
}
});
return IdeasView;
});
To use Masonry as a jQuery plugin with RequireJS, you’ll need to run jQuery bridget.
Check the documentation:
http://masonry.desandro.com/appendix.html#requirejs
You can download bridget here:
https://github.com/desandro/jquery-bridget
Then you can include an run bridget and it should work fine
define([
'jquery'
, 'underscore'
, 'backbone'
, 'config'
, 'app'
, 'jquery.masonry'
, 'jquery.bridget'
], function ($, _, Backbone, Config, App, Masonry, bridget) {
initialize : function () {
bridget('masonry', Masonry);
}
});
Hope it helps!
This is one way you can run it without bridget:
requirejs-config.js:
var config = {
paths: {
"lib-masonry": "Module/js/lib/masonry.pkgd.min"
},
shim: {
"lib-masonry": {
deps: ['jquery', 'jquery/ui']
}
}
}
and then in another module where you want to use it:
define([
'jquery',
'lib-masonry'
], function ($, Masonry) {
$.widget('ModuleNamespace.containerMasonry', {
options: {
},
_create: function() {
var msnry = new Masonry('.grid', {
itemSelector: '.grid__item',
columnWidth: 200
});
// .. code
}
});
return $.ModuleNamespace.containerMasonry;
});

Configuring require.js for backbone-ui

I am trying to use the backbone-ui library but cannot figure out the require.js configuration to get the modules loaded.
main.js:
requirejs.config({
baseUrl: '/static/js/facebook_report_app/js',
paths: {
backbone: 'lib/backbone'
, underscore: 'lib/underscore'
, jquery: 'lib/jquery'
, laconic: 'lib/laconic'
, moment: 'lib/moment'
, backboneUI: 'lib/backbone-ui/js/backbone_ui'
, menuUI: 'lib/backbone-ui/js/menu'
, textUI: 'lib/backbone-ui/js/text_field'
, text: 'lib/text'
},
shim: {
'lib/underscore': {
exports: '_'
},
'laconic': {
deps: ["jquery"],
exports: "$.el"
},
'lib/backbone': {
deps: ['lib/underscore']
, exports: 'Backbone'
},
'backboneUI': {
deps: ['lib/backbone', 'laconic', 'jquery']
, exports: 'Backbone.UI'
},
'textUI': {
deps: ['jquery', 'lib/backbone', 'backboneUI', 'laconic']
, exports: 'Backbone.UI.TextField'
},
'menuUI': {
deps: ['lib/backbone', 'backboneUI', 'laconic', 'textUI']
, exports: 'Backbone.UI.Menu'
},
'lib/backgrid': {
deps: ['lib/underscore', 'lib/backbone']
, exports: 'Backgrid'
},
'report_app': {
deps: ['lib/underscore', 'lib/backbone', 'lib/backgrid', 'backboneUI']
}
}
});
require([
'facebook_report_app'
],
function(FacebookReportApp) {
window.fbReport = new FacebookReportApp();
});
menu_user.js
define(['jquery', 'lib/backbone', 'backboneUI', 'menuUI', 'laconic'], function(AccountPickerView) {
var AccountPickerView = Backbone.UI.Menu.extend({
el: '.left-nav',
});
return AccountPickerView;
});
When I load this in dev, the console reports "Object [object Object] has no method 'input' ", on line 44 of the text_field.js of the Backbone-UI library.
I suppose my configuration approach is broken to begin with -- I added the menu.js and text_field.js files bc I was getting errors 'Backbone.UI.Menu' and Backbone.UI.TextField' (a requirement of Menu) weren't defined. But there must be a cleaner way to bring in the various files of backbone-ui.
So how do I get rid of the 'no method input' error? Or better configure to use Backbone UI? Or should I be using jQuery UI in the first place? In which case, where do I go to figure out configuration of that?
I got it working, after much fiddling with the following setup:
js/main.js
requirejs.config({
baseURL: 'js',
urlArgs: "bust=" + (new Date()).getTime(),
shim: {
underscore: {
exports: '_'
},
jquery: {
exports: '$'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
laconic: {
exports: '$.el'
},
backbone_ui: {
deps: ['underscore', 'jquery', 'backbone', 'laconic', 'moment'],
exports: 'Backbone.UI'
}
},
paths: {
jquery: 'http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min',
moment: './lib/moment.min',
laconic: './lib/laconic',
underscore: 'http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min',
backbone: 'http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min',
backbone_ui: './lib/backbone-ui',
crypto: 'http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5',
templates: '../templates',
collections: './collections',
models: './models',
}
});
js/views/json.js
define(['jquery','underscore', 'backbone', 'backbone_ui'],
function($,_,Backbone, BBUI){
var JsonView = Backbone.View.extend({
className: "json-item",
initialize: function(data){
var self = this;
this.app = data.app;
this.model = data.model;
this.listenTo(this.model, "change", function(){
console.log("model changed", self.model);
});
this.model.fetch({
success: function(){
self.render();
}
});
},
render: function(){
var self = this;
$(this.el).empty();
for (key in this.model.attributes){
this.el.appendChild(new Backbone.UI.Label({content:key}).render().el);
this.el.appendChild(new Backbone.UI.TextField({model: this.model, content:key}).render().el);
}
this.el.appendChild(new Backbone.UI.Button({content:"save", onClick: function(){self.model.save()}}).render().el);
return this;
}
});
return JsonView;
});
Please note that I import backbone-ui as BBUI and never reference it that way. BBUI.TextField() is a defined function and using it would work as well, but the way Backbone-ui is set up it makes alterations to Backbone, Jquery, and Underscore when it loads. So I figured I just needed it to be run prior to the view getting loaded.

Getting blank page using RequireJS and Grunt

I'm going a bit crazy here. I'm trying to use Grunt to go through a large RequireJS-based project and combine and minify the results during the deployment process. Here is my grunt process (using grunt-contrib-requirejs):
requirejs: {
compile: {
options: {
baseUrl: "public/js",
mainConfigFile: "public/js/config.js",
name: 'config',
out: "public/js/optimized/script.min.js",
preserveLicenseComments: false
}
}
}
Initially, I was taking the outputted script and placing it in the HTML -- but this lead to the 'define is undefined' error that means that RequireJS wasn't evoked. So instead, I'm putting in the HTML like this:
<script data-main="js/optimized/script.min" src="js/vendor/require.js"></script>
However, now I'm only getting a blank page.
The closest thing I can find out there that sounds like this is here, but's not being super helpful right now. For reference, I was using this as a starting point of my project -- however, when I run it, everything seems to be working for them but I can't find the differences.
Here is my config.js file:
require.config({
//Define the base url where our javascript files live
baseUrl: "js",
//Increase the timeout time so if the server is insanely slow the client won't burst
waitSeconds: 200,
//Set up paths to our libraries and plugins
paths: {
'jquery': 'vendor/jquery-2.0.3.min',
'underscore': 'vendor/underscore.min',
'backbone': 'vendor/backbone.min',
'marionette': 'vendor/backbone.marionette',
'mustache': 'vendor/mustache.min',
'bootstrap': 'vendor/bootstrap.min',
'bootbox': 'vendor/bootbox.min',
'jquery-ui': 'vendor/jquery-ui-1.10.2',
'app-ajax': '../conf/app-ajax',
'common': 'common',
'moment': 'vendor/moment.min'
},
//Set up shims for non-AMD style libaries
shim: {
'underscore': {
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'marionette': {
deps: ['backbone', 'underscore', 'jquery'],
exports: 'Marionette'
},
'mustache': {
exports: 'mustache'
},
'bootstrap': {
deps: ['jquery']
},
'bootbox': {
deps: ['jquery', 'bootstrap'],
exports: 'bootbox'
},
'jquery-ui': {
deps: ['jquery']
},
'jquery-layout': {
deps: ['jquery', 'jquery-ui']
}
}
});
//Initalize the App right after setting up the configuration
define([
'jquery',
'backbone',
'marionette',
'common',
'mustache',
'bootbox',
'controllers/GlobalController',
'routers/GlobalRouter'
],
function ($, Backbone, Marionette, Common, Mustache, bootbox) {
//Declare ECMAScript5 Strict Mode first and foremost
'use strict';
//Define the App Namespace before anything else
var App = Common.app_namespace || {};
//Create the Marionette Application
App.Application = new Marionette.Application();
//Add wrapper region, so we can easily swap all of our views in the controller in and out of this constant
App.Application.addRegions({
wrapper: '#wrapper'
});
// Set up Initalizer (this will run as soon as the app is started)
App.Application.addInitializer(function () {
//Reach into Marionette and switch out templating system to Mustache
Backbone.Marionette.TemplateCache.prototype.compileTemplate = function (rawTemplate) {
return Mustache.compile(rawTemplate);
};
var globalController = new App.Controllers.GlobalController();
var globalRouter = new App.Routers.GlobalRouter({
controller: globalController
});
//Start Backbone History
Backbone.history.start();
});
//Start Application
App.Application.start();
}
);
Okay, so this is the crazy simple fix:
In the module that's declared after the require.config, use 'require' instead of 'define' when declaring the module.
If you use 'define', it added 'config' as a dependency of that module, which broke the whole thing. Craziness!

Underscore.string with RequireJS

I'm trying to use both Underscore and Underscore.string with RequireJS.
Contents of main.js:
require.config({
paths: {
'underscore': '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min',
'underscore-string': '//cdnjs.cloudflare.com/ajax/libs/underscore.string/2.3.0/underscore.string.min',
},
shim: {
'underscore': {
exports: '_'
},
'underscore-string': {
deps: ['underscore'],
exports: '_s'
},
}
});
var modules = ['underscore-string'];
require(modules, function() {
// --
});
Browser sees the _, but doesn't see the _s - it is undefined.
Ideally i want to have Underscore under _ and Underscore.string under _.str, but _ and _s are fine too. How can i do that?
Versions: RequireJS 2.1.5, Underscore 1.4.4, Underscore.string 2.3.0
Note: Thanks to #jgillich make sure, that paths have two slashes (//cdnjs.cloudfare.com/...), otherwise the browser would think that URL is relative to the server, and Firebug will throw:
Error: Script error
http://requirejs.org/docs/errors.html#scripterror
I found the error. For some reason RequireJS doesn't work with version of Underscore.string from cdnjs.com, so i replaced it with Github version. I guess it has something to do with the commit 9df4736.
Currently my code looks like the following:
require.config({
paths: {
'underscore': '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min',
'underscore-string': '//raw.github.com/epeli/underscore.string/master/dist/underscore.string.min',
},
shim: {
'underscore': {
exports: '_'
},
'underscore-string': {
deps: ['underscore'],
},
}
});
var modules = ['underscore', 'underscore-string'];
require(modules, function(_) {
// --
});
Underscore.string resides in _.str.
Edit: As of 16 July 2013 the CDNJS version is updated with the upstream.
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'
works for my ONLY if I use exact "underscore.string" module name in shim. Seems related to hardcoded name in underscore.string itself
Exempt from underscore.string source code (this branch is executed when require used):
// Register as a named module with AMD.
if (typeof define === 'function' && define.amd)
define('underscore.string', [], function(){ return _s; });
So for me the only working configuration is:
require.config({
paths: {
'underscore': '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min',
'underscore.string': '//raw.github.com/epeli/underscore.string/master/dist/underscore.string.min',
},
shim: {
'underscore': {
exports: '_'
},
'underscore.string': {
deps: ['underscore'],
},
}
});
var modules = ['underscore', 'underscore.string'];
require(modules, function(_) {
// --
});
Here's a working code using Requirejs "order" plugin, also includes Jquery, and everything loads without any conflict:
requirejs.config({
baseUrl: "assets",
paths: {
order: '//requirejs.org/docs/release/1.0.5/minified/order',
jquery: 'http://code.jquery.com/jquery-2.1.0.min',
underscore: '//underscorejs.org/underscore-min',
underscorestring: '//raw.githubusercontent.com/epeli/underscore.string/master/dist/underscore.string.min',
underscoremixed: 'js/underscore.mixed' // Create separate file
},
shim: {
underscore: { exports: '_' },
underscorestring: { deps: ['underscore'] }
}
});
require(['order!jquery','order!underscoremixed'], function($,_) {
// test
console.log( _.capitalize('capitalized text') );
});
Inside js/underscore.mixed.js put the following...
define(['underscore','underscorestring'], function() {
_.mixin(_.str.exports());
return _;
});
Cheers! :)

require js backbone js jquery template markup rely

I have an underscore JS template and a jQuery plug-in that depends on the markup that gets rendered after the template is visible, but the problem is when I use jQuery document ready, it doesn't get the HTML that is rendered.
I used domReady but still ain't working, how can I accomplish this probably? This is what I got so far:
main.js
requirejs.config({
baseUrl: 'js/modules',
shim: {
// 'jquery.colorize': ['jquery'],
// 'jquery.scroll': {
// deps: ['jquery'],
// exports: 'jQuery.fn.colorize'
// },
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
deps: ['jquery'],
exports: '_'
}
},
paths: {
'jquery': '../libs/jquery',
'underscore': '../libs/underscore',
'backbone': '../libs/backbone',
'text': '../libs/text',
'views': '../views',
'templates': '../../templates',
'domReady': '../libs/domReady',
'ui': '../ui'
}
});
requirejs(['homeView', 'ui/placeholder'], function(Main){
Main.initialize();
});
homeView.js
define(['jquery', 'underscore', 'backbone', 'text!templates/home.html'], function($, _, Backbone, homeTemplate){
var HomeView = Backbone.View.extend({
el: $("#application"),
events: function(){
// Empty.
},
initialize: function(){
// Empty.
},
render: function(){
$('body').addClass('pt-dark');
$(this.el).html(_.template(homeTemplate, {}));
}
});
return new HomeView;
});
ui/placeholder.js
requirejs(['domReady', 'jquery'], function(doc, $){
doc(function(){
// Empty should contain the template html at this point.
console.log($('body').html());
});
});
Thanks a lot for help!
Try this:
define(['jquery', 'underscore', 'backbone', 'text!templates/home.html'], function($, _, Backbone, homeTemplate){
var HomeView = Backbone.View.extend({
el: $("#application"),
template: _.template(homeTemplate),
events: function(){
// Empty.
},
initialize: function(){
// Empty.
},
render: function(){
$('body').addClass('pt-dark');
this.$el.html(this.template({})));
jQuery('#the_thing_you_need', this.$el).theFunctionYouNeed();
}
});
return new HomeView;
});
Note that you should define a local attribute to the view called 'template' or something of that sort to store your template in and not re-call _.template every time that you need it.
Also, instead of jQuery(this.el) you can use this.$el.

Categories