I have seen examples of people calling this.render() when listening to changes to the model.
initialize : function() {
this.listenTo(this.model, "change:someAttribute", this.render());
this.listenTo(this.model, "change:someOtherAttribute", this.render());
}
if the render() function is creating a view from some underscore template and attaching it to the html document, what happens to the existing HTML that was attached originally? If I had a drop down selected and some text in one of the view's fields, why aren't they reset to the default values when the render() function is called?
What happens to your DOM depends on what you do in your render method, if you replace a whole elements html with
this.$el.html( _.template( htmlString, data ) );
of if you just append to it
this.$el.append( _.template( htmlString, data ) );
There are different scenarios based on how you set up your app, but i think you will mostly have the render method replacing the whole element, and then you will have an event listener bound to collection changes for example, so if a model is added to the collection you append a sub view to the main views el.
var view1 = Backbone.View.extend({
el: '#view1div',
initialize: function(){
this.collection.bind('reset', this.render, this);
this.collection.bind('add', this.addElement, this);
},
render: function(){
this.$el.html( '<ul id="list"></ul>' );
},
addElement: function(){
$('#list').append( '<li>something</li>' );
}
});
Related
This question already has an answer here:
Backbone click event not firing in template View
(1 answer)
Closed 6 years ago.
I am getting the entire html code using jquery get() method and setting it on the el of backbone view.The view gets rendered perfectly but the click events i added are not firing. As i am a newbie to backbone i am not able to find the issue. Any help would be appreciated.
The currentTabID is contain the div id on which i want this html to be rendered.
view.js
var MyFirstView = Backbone.View.extend({
currentTabID:'',
initialize:function(){
this.render();
},
render: function (){
var self = this;
self.el = self.options.currentTabID;
$.get('resources/html/myBB.html', function(data) {
$(self.el).html(_.template(data));
});
return this;
},
events: {
'click .savebtnBB': 'invokeME'
},
invokeME: function (){
console.log('Fired');
}
});
Html looks something like below
myBB.html
<div id="sample_tab">
<div class="sub-main">
<form>
..
</form>
</div>
<div class="button">
<button class="savebtnBB">click me</button>
</div>
</div>
view.el is an actual dom element holding the event listeners for your view. You're replacing view's reference to that element with some number and appending the template to some other element.
Your view should act like an isolated unit as much as possible. Your code for appending it to something else should be outside the view, where you're creating it. Your code should look something like the following:
var MyFirstView = Backbone.View.extend({
initialize: function() {
var self = this;
$.get('resources/html/myBB.html', function(html) {
self.template = _.template(html);
this.render();
});
},
events: {
'click .savebtnBB': 'invokeME'
},
render: function() {
this.$el.html(this.template({ /*some data for template*/ }));
},
invokeME: function() {
console.log('Fired');
}
});
var viewInstance = new MyFirstView();
/*append to whatever you want*/
$(currentTabID).append(viewInstance.el);
This is the code:
NewEntry_CategoryView = Backbone.Marionette.ItemView.extend({
template: "#NewEntry_Category-template",
tagName: "p",
initialize: function () {
$("#sliderContainer").slider();
}
});
NewEntry_CategoriesView = Backbone.Marionette.CompositeView.extend({
template: "#NewEntry_Categories-template",
tagName: "div",
itemView: NewEntry_CategoryView,
itemViewContainer: '#categoryContainer',
appendHtml: function (collectionView, itemView) {
collectionView.$("#categoryContainer").append(itemView.el);
}
});
Why does the jquery ui slider not render when I show the NewEntry_CategoriesView ?
DOM events/manipulation like slide() won't have any effect on the view object's initialization because there is no such DOM element available yet.
Instead, you need to listen to dom:refresh of the view to manipulate its DOM element.
So, just put the code in onDomRefreshin your ItemView
onDomRefresh: function(){ $('#sliderContainer').slide() };
This above is a direct fix. But there are two more things to improve:
Don't call other div outside of this view when possible. In this case, if #sliderContainer belongs to another view, send an event to allow it slide itself. This is not the job of CategoryView. If it is inside current view, refer it with this.$el.find(".some-div") or better yet ui object.
Your collectionView's appendHtml is unnecessary. Marionette also takes of this common case.
I am using twitter bootstrap link. When the user clicks the link a bootstrap modal appears.
Now because of some bootstrap technical difficulties in modal rendering i need to seperate the link and put the modal out the navbar div.
So consider i have two separate div
<div id="linkDiv">
</div>
and
<div id="modalDiv">
</div>
Now i have only one View which makes a call to the server to get the collection
app.View.FriendRequestListView = Backbone.View.extend( {
templateModalLink: _.template($('#link').html()),
templateModal: _.template($('#modal').html()),
tagName: 'div',
initialize: function(){
this.friendRequestCollection = new app.Collection.FriendRequestCollection();
this.friendRequestCollection.bind("reset", this.render, this);
this.friendRequestCollection.fetch();
},
render: function() {
$(this.el).html(this.templateModalLink({
friendRequestCollection: this.friendRequestCollection}));
return $(this.el);
},
});
Than i can render only one div like following
var list = new app.View.FriendRequestListView();
$('#linkDiv').html(list.$el);
My question is , Is it possible to render two templates at the same time and add the two templates to different DIV like for example in my case i want to get update
templateModalLink template to linkDiv and templateModal template to modalDiv with the collection I am getting from the server.
You have to instantiate the collection before app.View.FriendRequestListView(s) and pass app.View.FriendRequestListView(s) the collection:
var friendRequests = new app.Collection.FriendRequestCollection();
friendRequests.fetch(
success: function(collection, response, options) {
var list1 = new app.View.FriendRequestListView({collection: collection});
var list2 = new app.View.FriendRequestListView({collection: collection});
$('#linkDiv').html(list1.$el);
$('#modalDiv').html(list2.$el);
}
);
I am a backbone newbie and I am trying to develop a Todo like App.
I have a Main view which is a list view and it has subviews. - the subview content can be edited on double click and it would get saved when the enter key is pressed. - very similar to todo example given in backbone github code.
var SubView = Backbone.View.extend({
tagName: "li",
events: {
"dblclick" : "show_edit_view",
"blur .element" : "close_edit_view",
"keypress .element" : "save_edit_view",
"click button.remove" : "remove_question"
},
initialize: function(){
this.render();
this.listenTo(this.model, "change", this.render);
},
render: function(){
this.$el.html(_.template($("#sub_view_template").html(),this.model.toJSON()));
return this;
},
show_edit_view: function() {
this.$el.find("div.view").addClass("no_show");
this.$el.find("input").removeClass("no_show");
},
close_edit_view: function(){
this.$el.find("div.view").removeClass("no_show");
this.$el.find("input").addClass("no_show");
},
save_edit_view: function(e){
if (e.keyCode == 13) {
this.model.save({name: e.currentTarget.value});
this.close_edit_view();
}
}
});
And the template for this is
<script id="sub_view_template" type="text/x-template">
<div class="view"><%= name %></div>
<input class="element no_show" value="<%= name %>" type="text" /> <button class="remove">Remove</button>
</script>
This one works fine, the model is updated in the view and the update post request is sent to the server.
But, when I change the initialization and save_edit_view functions, only the first change event is fired and not the change events.
initialize: function(){
this.render();
this.listenTo(this.model, "change", this.render);
this.input = this.$("input.element");
},
save_edit_view: function(e){
if (e.keyCode == 13) {
this.model.save({name: $(this.input).val()});
this.close_edit_view();
}
}
I was wondering what could the problem be?
Thanks for any help!!!
The problem is you are referring to only one object. This means when you make the assignment:
this.input = this.$('input.element'); // match the current elements.
You are only getting the value from that exact object. After the first change, this.input is not the same object that contains your new value, and fails to save the model with a new value.
A demonstration that may help:
console.log(this.$('input.element') != this.$('input.element')); // true
This is why the following would work:
save_edit_view: function(e){
if (e.keyCode == 13) {
this.model.save({name: this.$('input.element').val()});
this.close_edit_view();
}
}
I guess this.$("input.element"); refers to the first item from the list.
And when you first time change model value with the value from the first item it works. But second time it doesn't works because the value of the first item still the same.
That is why you have to get input value from the event - e.currentTarget.value
Take a HTML tabbar as example. Usually you have a ul and a list of div's. All the Backbone examples that I have found, link the View with only one node by the 'el', 'tagName', etc...
HTML TabBar:
<div class=".tabbar">
<ul class=".tabbar-header">
<li>Cars</li>
<li>Houses</li>
</ul>
<div id="tab-cars" class=".tabbar-item">...</div>
<div id="tab-houses" class=".tabbar-item">...</div>
</div>
Backbone Code:
window.TabBarView = Backbone.View.extend({
el: ???,
tabs: [],
render:function (eventName) {
// Render all tabs in this.tabs
_.each(this.tabs, function (item, position) {
// Render each tab with item.render()
}, this);
return this;
}
});
window.TabBarItemView = Backbone.View.extend({
el: ???,
initialize:function () {
this.model.bind("change", this.render, this);
this.model.bind("destroy", this.close, this);
},
render:function (eventName) {
// Render the tab header and tab content
return this;
}
});
I wish to add several TabBarItemView's to the TabBarView and each one creates itself the li node inside the ul.tabbar-header and the div.tabbar-item as content.
I've written an article that addresses this issue: http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
It will show you how you can either use a single view to do what you want, or a parent/child setup with a collection view and item view like you're showing in your sample code
you can go as far as to make a separate navigation view, and have the navigation add an item through the render method of your tab-item-view.
when you render the tab item view, you do something like navigation.add(new nav item);
and also add a way to remove the navigation item.
or you can keep the navigation in pure html and append a <li> item with jquery / javascript when you are rendering a tab below.
can't give you a fully working example though, if you really need it i can probably make one tonight,.