App.Views.VideoView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.model = this.options.model;
this.render();
},
render: function() {
JST.video({
model: this.model
});
return this;
}
});
App.Views.PlayListView = Backbone.View.extend({
el: $("#playlistWrapper"),
initialize: function(videos) {
_.bindAll(this, 'render');
this.modelViews = $.map(videos.models, function(model, i) {
return new App.Views.VideoView({
model: model
});
})
this.render();
},
render: function() {
var that = this;
$(this.el).clear();
$.each(this.modelViews, function(i, modelView) {
$(that).el.append(modelView.render().el);
});
return this;
}
});
i am always getting below error
$(this.el).clear is not a function
[Break On This Error] $(this.el).clear();
it seems my el of PlayerListView is empty.
i have div with id playlistWrapper.
if i use jquery selector for playlistWrapper it gives proper element.
what am i doing wrong.
I'm a little late to the party on this, but the problem is that you're specifying a jquery selector before the DOM is loaded.
A backbone object is defined with an object literal passed in to the extend method. For example, the following are functionally the same:
MyView = Backbone.View.extend({
el: "#foo"
});
var viewObj = {el: "#foo"};
MyView2 = Backbone.View.extend(viewObj);
values on the right-hand side of a key/value pair in an object literal are parsed and executed immediately. this means that a jQuery selector used for el will be parsed as soon as you declare it, not when the view is instantiated. chances are, you have your javascript file included in your app and it's being downloaded before the DOM is loaded, so the jquery selector can't find the element you're referring to.
There are a number of things you can do to work around this.
you can call $(this.el) whenever you need to use the element
you can set this.el in the view initializer
you can set {el: $("#whatever")} as a parameter to the view constructor, assuming the view is constructed after the DOM has loaded
you can use the javascript module pattern to defer definition of the views and other backbone objects until after the DOM is loaded
and probably a handful of other options that i'm not thinking of at the moment
Well clear is not a jQuery function... You might be looking for empty?
Comments on your code:
In you video view:
no need to assign the model from the options, this is done for you
you might want to append the result of the templating (JST) to this.el otherwise nothing will show up...
In your playlist view:
in your render, in your each loop, change $(that).el to $(that.el)
since you define el as a jQuery, you do not need to use $(this.el) over and over
use this.$el.clear();
and update jquery.js file like
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"> </script>
and first Bind Initialize then el: bind.
Related
Handlebars guide has an example of how to implement helper methods. But how can I render an already existing element instead of getting outerHTML of this element and return it as a string. I need this because of my Ember views setting events on elements and when I getting outerHTML it generates new dom elements, and events of views does not fire UP.
Ok I have backbone View that getting Handlebars precompiled template
SomeView = Backbone.View.extend({
initialise: function(){
this.render();
},
render: function(){
html = HandlebarsTemplates.some_template();
this.el.innerHtml = html;
},
raiseAlert: function(){
alert('Some message');
},
events: {
'click': 'raiseAlert'
}
})
And I have Handlebars helper
Handlebars.registerHelper 'show', function(){
var view = new SomeView({});
var result = view.el.outerHTML;
new Handlebars.SafeString(result)
}
So I want to not use outerHtml but insert view.el instead.
Background:
I am making changes to an application that uses backbone.js with Handlebars as the templating engine. After a change event fires I need to create html that is appended to the current DOM structure which is basically just a spit-out of information that is contained in the model. This change needed to fit in the already established application structure.
Issue:
I have created a new view that uses a Handlebars template and the model to create the html. I then instantiate that view and call the render function and append the output using JQuery. What I am noticing is that when the html is rendered the model that is passed in because attributes on the $el instead of filling in the template (like I think it should).
View I'm altering:
$.hart.TestView = Backbone.View.extend({
tagName: "li",
template: Handlebars.compile($('#templateOne').html()),
initialize: function () {
this.model.on('change', function () {
this.createMoreInfoHtml();
}, this);
},
selectSomething: function () {
this.$el.removeClass('policies');
this.createMoreInfoHtml(); //function created for new view stuff
},
createMoreInfoHtml: function () {
var id = this.$el.attr('data-id', this.model.get("ID"));
$('.info').each(function () {
if ($(this).parent().attr('data-id') == id
$(this).remove();
});
var view = new $.hart.NewView(this.model, Handlebars.compile($("#NewTemplate").html()));
$('h1', this.$el).after(view.render().el);
},
render: function () {
... //render logic
}
});
View I Created:
$.hart.NewView = Backbone.View.extend({
initialize: function (model, template) {
this.model = model;
this.template = template;
},
render: function () {
this.$el.html(this.template({ info: this.model }));
this.$el.addClass('.info');
return this;
}
});
Json the is the model:
{
"PetName":"Asdfasdf",
"DateOfBirth":"3/11/2011 12:00:00 AM",
"IsSpayNeutered":false,
"Sex":"F",
"SpeciesID":2,
"ID":"ac8a42d2-7fa7-e211-8ef8-000c2964b571"
}
The template
<script id="NewTemplate" type="text/html">
<span>Pet Name: </span>
<span>{{this.PetName}}</span>
</script>
So now to the question: What am I doing wrong? Why are the properties of the model being created as attributes on the $el instead of filling in the template? Can someone please direct me as to how to get the results I am looking for?
Let's skip the problem Jack noticed.
The way you're creating your view is just wrong. It may work as you get the expected arguments in the initialize function, but it has unexpected behaviors you don't see. See the View's constructor:
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
this._configure(options || {});
Now let's have a look at this _configure method:
_configure: function(options) {
if (this.options) options = _.extend({}, _.result(this, 'options'), options);
_.extend(this, _.pick(options, viewOptions));
And of course...
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
Ok here we are... Basically when passing the model as the options argument, you're passing an object with an attributes key (the attributes of your model). But this attributes key is also used in the View to bind attributes to its element! Therefore the behavior your noticed.
Now, other wrong thing. You're compiling your template each time you create a new function, but not using it as a singleton either. Put your template in the view:
$.hart.NewView = Backbone.View.extend({
template: Handlebars.compile($("#NewTemplate").html(),
And change your view's creation to make the whole thing work:
new $.hart.NewView({model: this.model});
Oh, and get rid of this useless initialize method. You're just doing things Backbone already does.
I have a Backbone view (see below) that I believe to be doing the right thing
Index = Backbone.View.extend({
render: function() {
var activities = new Activities();
activities.fetch();
var tpl = Handlebars.compile($("#activities-template").html());
$(this.el).html(tpl({activities: activities.toJSON()}));
return this;
}
});
If execute each line in the render() function with Chrome JS console I get the expected result with the element I pass in getting populated with the template output. However, when I run this using the following
var i = new Index({el: $("body")})
i.render()
"i.$el" is completely empty--the HTML is not getting rendered like it does in console. Any ideas why?
fetch is an AJAX call so there's no guarantee that activities.toJSON() will give you any data when you do this:
activities.fetch();
var tpl = Handlebars.compile($("#activities-template").html());
$(this.el).html(tpl({activities: activities.toJSON()}));
Executing the code in the console probably gives the AJAX call time to return with something before you try to use activities.
You should do two things:
Fix your template to do something sensible (such as show a loading... message of some sort) if activities is empty.
Attach your view's render to the collection's "reset" event:
initialize: function() {
// Or, more commonly, create the collection outside the view
// and say `new View({ collection: ... })`
this.collection = new Activities();
this.collection.on('reset', this.render, this);
this.collection.fetch();
},
render: function() {
var tpl = Handlebars.compile($("#activities-template").html());
this.$el.html(tpl({activities: this.collection.toJSON()}));
return this;
}
I also switched to this.$el, there's no need to $(this.el) when Backbone already gives you this.$el.
I have two views -- an OverlayView and a StoryView. The StoryView needs to get appended to the OverlayView (both are created dynamically). I create the #overlay div dynamically in the OverlayView, but when I set the 'el' of the StoryView to #overlay, this.$el of StoryView is an empty array. The #overlay div definitely exists in the DOM by the time the StoryView is created.
How do I get the StoryView to recognize the dyanmically created #overlay as its 'el'? Am I correct in assuming the 'el' should be the 'parent' container to which the view is appended? Should the 'el' of the OverlayView actually be '#overlay'?
OverlayView:
OverlayView = Backbone.View.extend({
el: $('body'),
events: {
'click #film': 'hideOverlay'
},
initialize: function() {
this.render();
},
render: function() {
this.$el.append('<div id="overlay"></div>');
return this;
}
});
StoryView:
var StoryView = Backbone.View.extend({
el: $('#overlay'),
events: {
'click .close': 'closeStory'
},
initialize: function() {
this.render();
},
render: function() {
console.log(this.$el); // Returns empty array []
return this;
}
});
Your problem is that your StoryView el should simply be a selector — not a jQuery object. That will cause Backbone to try to retrieve the object (which does not yet exist) at the time you specify el. Just change el: $('#overlay') to el: '#overlay'. You might also do the same for $('body') in your first view, since it's not necessary. Just always use selectors instead of jQuery objects and you'll be fine.
Working example here.
I'm having a look at Backbone.js, but I'm stuck. The code until now is as simple as is possible, but I seem not to get it. I use Firebug and this.moments in the render of MomentsView is an object, but all the methods from a collection don't work (ie this.moments.get(1) doesn't work).
The code:
var Moment = Backbone.Model.extend({
});
var Moments = Backbone.Collection.extend({
model: Moment,
url: 'moments',
initialize: function() {
this.fetch();
}
});
var MomentsView = Backbone.View.extend({
el: $('body'),
initialize: function() {
_.bindAll(this, 'render');
this.moments = new Moments();
},
render: function() {
_.each(this.moments, function(moment) {
console.log(moment.get('id'));
});
return this;
}
})
var momentsview = new MomentsView();
momentsview.render();
The (dummy) response from te server:
[{"id":"1","title":"this is the moment","description":"another descr","day":"12"},{"id":"2","title":"this is the mament","description":"onother dascr","day":"14"}]
The object has two models according to the DOM in Firebug, but the methods do not work. Does anybode have an idea how to get the collection to work in the view?
The problem here is that you're fetching the data asynchronously when you initialize the MomentsView view, but you're calling momentsview.render() synchronously, right away. The data you're expecting hasn't come back from the server yet, so you'll run into problems. I believe this will work if you call render in a callback to be executed once fetch() is complete.
Also, I don't think you can call _.each(this.moments) - to iterate over a collection, use this.moments.each().
Try removing the '()' when instantiate the collection.
this.moments = new Moments;
Also, as it's an asynchronous call, bind the collection's 'change' event with the rendering.
I hope it helps you.