Getting this odd error when using node-webkit, here is a complete example which can recreate it:
index.js:
var jQuery = require('jquery');
var Backbone = require('backbone');
(function($){
var ListView = Backbone.View.extend({
el: $('body'),
initialize: function(){
_.bindAll(this, 'render');
this.render();
},
render: function(){
$(this.el).append("<ul> <li>hello world</li> </ul>");
}
});
var listView = new ListView();
})(jQuery);
index.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="./css/main.css" />
</head>
<body>
</body>
<script src="./js/index.js"></script>
</html>
The stack trace:
TypeError: Expecting a function in instanceof check, but got undefined
at _.extend.setElement (C:\SVN\mapthing\branches\mapthing_js\node_modules\backbone\backbone.js:1046:45)
at _.extend._ensureElement (C:\SVN\mapthing\branches\mapthing_js\node_modules\backbone\backbone.js:1108:14)
at Backbone.View (C:\SVN\mapthing\branches\mapthing_js\node_modules\backbone\backbone.js:1000:10)
at new child (C:\SVN\mapthing\branches\mapthing_js\node_modules\backbone\backbone.js:1566:41)
at file:///C:/SVN/mapthing/branches/mapthing_js/js/index.js:18:18
at file:///C:/SVN/mapthing/branches/mapthing_js/js/index.js:19:3
Having trouble seeing where my problem is?
Most solutions seem to point to having to require jquery first, but I have done this...
I'm a relative n00b so I'm expecting ive done something very daft...
This is a hack, so be warned of that, but the error is coming from this method in the backbone lib (node_modules/backbone/backbone.js:1046 from your stacktrace):
setElement: function(element, delegate) {
if (this.$el) this.undelegateEvents();
this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
this.el = this.$el[0];
if (delegate !== false) this.delegateEvents();
return this;
},
Most likely you can circumvent this error by explicitly setting jQuery on Backbone, like this:
var jQuery = require('jquery');
var Backbone = require('backbone');
Backbone.$ = jQuery;
That is a totally untested guess, but it should work.
Related
Previously this will work but I've update the underscore and backbone to the latest version, then I got error of
Uncaught TypeError: this.$el.off is not a function
http://jsfiddle.net/mmm770v8/
SearchView = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
var template = _.template( $("#search_template").html(), {} );
this.el.html( template );
},
events: {
"click input[type=button]": "doSearch"
},
doSearch: function(){
// Button clicked
console.log(this.el.find('#search_input').val());
}
});
You have several problems:
Your fiddle was using jQuery 1.5.2 which is ancient and used bind/unbind instead of on/off. Backbone expects a more recent version of jQuery which has on and off functions.
You're using this.el where you mean this.$el. this.el is just a plain old DOM node, this.$el is the cached $(this.el).
The var html = _.template(tmpl, data) form of _.template went away in Underscore 1.7.0. You now need a two step process:
var t = _.template(tmpl);
var h = t(data);
so your render should look more like this:
render: function() {
var template = _.template($("#search_template").html());
this.$el.html(template({}));
}
Updated fiddle: http://jsfiddle.net/ambiguous/L5z4agh4/
Having understood how Backbone.js communicates with the server, I'm now having trouble with Backbone.View.render().
My javascript code is this:
var myObject = Backbone.Model.extend( {
fetch: function (options) {
// some modifications
}
});
var Templates = myObject.extend({
urlRoot: 'templates/load.php',
initialize: function() {
this.fetch();
}
});
var appTemplates = new Templates();
load.php loads all template files and transforms them to one JSON object that is then returned to the client. So appTemplates will have all my Mustache.js templates ready for the page.
To test that, I created a 'main' template and a View:
var Output = Backbone.View.extend({
template: function() {
return appTemplates.get('main');
},
render: function() {
var rendered = Mustache.to_html(this.template(), this.model.toJSON() );
console.log( rendered ); // has exactly the data I want.
this.$el.html( rendered ); // <body> ... </body> remains empty
$('body').html( rendered ); // works fine.
return this;
},
});
var skeleton = new Output({ el: 'body', model: SomeModelData });
skeleton.listenTo(appTemplates, 'change', skeleton.render);
So why doesn't this.$el.html() work? I thought, this.$el is just a shortcut for $(this.el) but this.el is undefined, regarding the output of console.log (I did not define it? Thought I did...) while this.model works just fine.
The initial HTML is not very spectacular:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="./javascript/jquery-2.1.4.js" ></script>
<script src="./javascript/underscore.js"></script>
<script src="./javascript/backbone.js" ></script>
<script src="./javascript/mustache.min.js"></script>
<script src="./javascript/js.js"></script>
<title></title>
</head>
<body>
</body>
</html>
There is another issue with the 'main' string in in the View constructor. If I send 'main' as a value to the constructor ( { tpl: 'main', ... } ) tpl remains undefined.
What am I doing wrong here?
Edit: Problem solved
As it seems, the problem was that my js-file was included within the <head>...</head> of the html but it needs to be included within <body>...</body> to work as expected. If someone knows a reason for this I would love to hear about it.
I use the backbone marionette in browserify.
I met a problem about showing view.
I have addRegions and want to show the ItemView.
But the console show error: Uncaught Error: An "el" #tmp_area must exist in DOM.
My HTML file have #tmp_area area.
When I use require.js it doesn't show this problem, but it happened when I change to use browserify.
I don't know what's wrong.
Below is my code.
app.js
var Backbone = require('backbone');
var $ = require('jquery');
Backbone.$ = $;
var Marionette = require('backbone.marionette');
var MyView = require('./views/my_view');
var app = new Marionette.Application();
app.addRegions({
tmp_area: "#tmp_area"
});
app.addInitializer(function() {
var myView = new MyView();
app.tmp_area.show(myView);
});
app.on("initialize:after", function() {
if (Backbone.history) {
Backbone.history.start();
}
});
app.start();
my_view.js
var $ = require('jquery');
var Backbone = require('backbone');
var Marionette = require('backbone.marionette');
var templates = require('../templates/tmp.hbs');
Backbone.$ = $;
module.exports = Marionette.ItemView.extend({
template: templates,
})
Please help me!
I do appreciate it!
I'm pretty sure that this is related to the document ready state. You can just put a script at the bottom of your HTML or wrap an application initialization step into document.ready function.
Im new to Backbone (dont hate me) but am pulling my hair out trying to do a very simple thing.
Im loading a json file (correctly as I can see it loading in firebug) and I just want to pull some info from it purely for testing (as its my first backbone code)
However, I cant get this working and end up with one blank li tag (code below)
<ul id="phones"></ul>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.rc.1/handlebars.min.js"></script>
<script id="foo" type="text/template">
<li><%= name %></li>
</script>
<script>
var Phones = Backbone.Collection.extend({
url:'http://backbone.local/phones/phones.json'
})
var PhonesView = Backbone.View.extend({
el:'#phones',
initialize:function(){
this.collection = new Phones();
this.collection.fetch();
this.render();
},
template:_.template($('#foo').html()),
render:function(){
var foo = this.collection.toJSON();
$(this.el).html(this.template(foo));
return this;
}
})
var phonesView = new PhonesView();
</script>
Any pointers greatly appreciated.
Cheers,
UPDATE 1
I thought it may be due to fetch being async so i called render in success callback of fetch as below. The console.log fires fine but still no json data in rendered html (i also changed to using handlebars)
<script>
var Phones = Backbone.Collection.extend({
url:'http://backbone.local/phones/phones.json'
})
var PhonesView = Backbone.View.extend({
el:'#phones',
initialize:function(){
var self = this;
this.collection = new Phones();
this.collection.fetch({
success:function(){
console.log('json loaded');
self.render();
}
});
},
template: Handlebars.compile('<li>sdsadsadsadsad {{name}} dsfcdfd</li>'),
render:function(){
var foo = this.collection.toJSON();
$(this.el).html(this.template(foo));
return this;
}
})
var phonesView = new PhonesView();
</script>
With Handlebars, a collection template looks like this:
{{#items}} <li> {{name}} </li> {{/items}}
You also need to wrap your collection JSON in an items object so that the Handlebars template can reference it as above:
var foo = { items: this.collection.toJSON() };
Edit
There's actually one more issue ... collection.toJSON() doesn't convert each model to JSON. So you need to write:
this.collection.models.map(function(x) { return x.toJSON(); });
Fiddle Demo
On your view:
'initialize': function() {
this.template = _.template('<p><% model.text $></p>');
this.collection.fetch({error: function() { console.log(arguments); }, 'success': _.bind(this.onFetch,this) });
return this;
},
'onFetch': function(collection) {
this.collection.on("add", this.onAdd, this);
this.collection.each( _.bind(this.onAdd, this) );
return this;
},
'onAdd': function(model){
//do something like:
this.$el.find('.items').append(this.template({model: model}) )
);
To answer your explicit question of why you only get an empty li. You must send the template some name data. For example:
render:function(){
var firstModel = this.collection.at(0);
var firstName = firstModel.get("name");
$(this.el).html(this.template({name: firstName}));
return this;
}
Of course, the above code is only to understand what's missing, and not how a backbone application should be implemented. I really recommend that you go over the annotated TODO example linked from Backbone's website, and understand the basic patterns that are implemented there.
Update based on your comment:
Different ways of solving this. Really recommend reading: http://backbonejs.org/docs/todos.html
To continue on the "hacky path" of solving this so you can see something:
addOne: function(phone) {
this.$el.append(this.template(phone.toJSON()));
return this;
},
render: function() {
this.$el.html(); //clear everything in the view
this.collection.forEach(_.bind(this.addOne,this)); //Will call addOne for every model in the collection.
return this;
}
I'm new to Backbone and I'm trying to get a basic view rendered using Underscore's templating. Here's the javascript:
TestView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
var template = _.template( $('#template').html(), {} );
this.el.html( template );
}
});
var test_view = new TestView( { el: $('#container') } );
This is the error I'm getting in Chrome:
Uncaught TypeError: Expecting a function in instanceof check, but got [object Object]
It's throwing the error from Backbone on line 1203 (development version). You can see the error in action at my website.
What am I doing wrong here? Should I omit the render() function?
Change the loading order, i.e. jQuery first:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore.js"></script>
<script src="http://backbonejs.org/backbone.js"></script>