Unable to Create Backbone View - javascript

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>

Related

How to use RequireJS with Knockout

My setup so far:
<script src="/common/js/require.configure.js"></script>
<script src="/common/js/lib/require.js" data-main="/common/js/app.js"></script>
require.configure.js
var require = {
baseUrl: '/',
paths: {
"jquery": "/common/js/lib/jquery",
"fastclick": "/common/js/lib/fastclick",
"knockout": "/common/js/lib/knockout",
"common": "/common/js/scripts/common"
}
};
The top three paths are obviously just libraries that I am using for my application. The last file "common" is a collection of functions that are global to my application such as opening the main menu, giving messages to the user, or binding handlers, etc.
app.js
define(["jquery", "knockout", "fastclick", "common"], function (){
});
I know that requireJS always needs a data-main file to run initially. But what does this above code really do? I trying to follow tutorials online but it is not helping. I'm guessing that by defining those strings in the array, it looks it up in the configuration file and is loading in those files, but how are those files then accessed or used? I'm guessing that I can simply then just "require" those same strings and they will be available to me in my functions?
common.js (simplified for Stack Overflow)
require(["knockout"], function (ko) {
var appViewModel = {};
appViewModel.loaded = ko.observable(false);
});
By wrapping everything in the require() I think that this is injecting the dependencies of needing knockout.
App's First Page - login.html (simplified for S.O.)
In the first page of the app, I define a <script> tag with the following
require(["jquery", "knockout", "fastclick", "common"], function ($, ko, FastClick)
{
$(function(){
appViewModel.loginData = {
email : ko.observable(),
password : ko.observable()
};
});
});
And the resulting error when trying to run is that
Uncaught ReferenceError: appViewModel is not defined
despite the fact that I have included "common" in the require([]).
What am I missing here? I think that I may be completely misunderstanding what "require" and "define" do in requireJS, so that would be a good basis of an answer for me.
i think you want to do something like that:
Modules that define global obj
require(["knockout"], function (ko) {
window.appViewModel = {};
window.appViewModel.loaded = ko.observable(false);
});
Modulw that popule the obj:
require(["jquery", "knockout", "fastclick", "common"], function ($, ko, FastClick)
{
window.appViewModel.loginData = {
email : ko.observable(),
password : ko.observable()
});

Using SammyJs with RequireJs

I'm having a bit of bother getting sammyjs to play with requirejs. Calling $.sammy fails and the error says that sammy is not defined in the jQuery namespace.
Here is my require config
require.config
baseUrl: '/Scripts'
waitSeconds: 10
paths:
bootstrap: './lib/bootstrap/bootstrap'
domReady: './lib/domReady/domReady'
knockout: './lib/knockout/knockout-2.2.1.debug'
jquery: './lib/jquery/jquery-1.9.1'
sammy: './lib/sammy/sammy-0.7.4'
myPage: './app/pages/myPage'
myViewModel: './app/viewModels/myViewModel'
shim:
bootstrap:
deps: ["jquery"]
sammy:
deps: ["jquery"]
exports: "Sammy"
Here's my page javascript
require ['knockout', 'myViewModel', 'domReady!' ], ( ko, viewModel ) ->
myViewModel = new viewModel
ko.applyBindings( myViewModel )
Here's my view model
define [ 'jquery', 'sammy', 'knockout' ], ( $, sammy, ko ) ->
class myViewModel
constructor: ( options ) ->
self = #
#sammypath = ko.observable( 1 )
#router = $.sammy( ->
#get '#/', ( data ) ->
self.sammypath( 1 )
)
#router.run()
However I get an error when I try to call $.sammy
Uncaught TypeError: Object function ( selector, context )
{ // The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery ); }
has no method 'sammy'
I guess it's something that's wrong with the require.config but to be honest I'm just not sure.
The sammy-0.7.4.js file is downloaded fine.
The sammy that is passed through to the define in the view model is not null
I stuck a break-point on the sammy file and it gets hit and recognizes that it's an AMD module. I'm just not sure why it doesn't add itself to the jQuery namespace.
I double checked that the page was loaded correctly first but the domReady module takes care of that.
I'm also using coffeescript but I don't think that that should be an issue.
To get sammy to work I just drop the $. notation since I'm injecting it
#router = sammy( ->
#get( '#/', ( data ) ->
self.sammypath( 1 )
)
)
I can't seem to get $.sammy to work unfortunately. The RequireJs documentation has some hints as to how to get $.sammy to work perhaps
var require = {
deps: ["some/module1", "my/module2", "a.js", "b.js"],
callback: function(module1, module2) {
//This function will be called when all the dependencies
//listed above in deps are loaded. Note that this
//function could be called before the page is loaded.
//This callback is optional.
}
};
It looks like I could use the callback to get this to work with something like this
var require = {
deps: ["jquery", "sammy"],
callback: ($, sammy) ->
$.sammy = sammy
}
But I can't get this to play with the require.config, if someone knows how to sort this let me know! I've seen other people do something like this with knockout and ko and the mapping plugin ko.mapping
Sammy should play nicely with requirejs and already know that it needs the jquery module loaded.
I'm new to sammyjs, so perhaps the answers above were before sammy was amd compatible?
This is a snippet of the current sammyjs version, which shows that it should be ok with requirejs... at least I've not had any trouble with it.
(function(factory){
// Support module loading scenarios
if (typeof define === 'function' && define.amd){
// AMD Anonymous Module
define(['jquery'], factory);
} else {
// No module loader (plain <script> tag) - put directly in global namespace
jQuery.sammy = window.Sammy = factory(jQuery);
}
})(function($){
Here is an example of using it
define(["jquery", "../sammy-0.7.5.min"], function ($, Sammy) {
Sammy('#main', function() {
// define a 'get' route that will be triggered at '#/path'
this.get('#/path', function() {
// this context is a Sammy.EventContext
this.$element() // $('#main')
.html('A new route!');
});
}).run();
}

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

How to use the domReady requireJS plugin correctly

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($) { }

Categories

Resources