why can't I get two instances of my view to render? - javascript

I'm a noob in backbone.js and JavaScript for that matter... and I'm trying to build a simple widget system with Jquery and backbone.js, but I can't seem to figure out how to get multiple instances of my view to render. I am, however able to get one instance to render... my ultimate goal is to build a system where i can click on a button and have it render a new widget on the screen each time.
here is my code:
<script type="text/template" id="widget-template">
<div class="widget-wrap">
<div class="widget-header">
<span class="widget-title"><%= widgetInfo.get('title') %></span>
<span class="widget-close"></span>
<span class="widget-hide"></span>
<span class="widget-expand"></span>
</div>
<div class="widget-container">
<!-- this is where the widget content goes -->
</div>
</div>
</script>
<script typ="text/javascript">
var baseWidget = Backbone.Model.extend({
defaults: {
title: "Base",
author: "AB",
multipleInstances: false,
description: "This is the base widget",
pathToIcon: "",
state: "Open",
position: {left:0, top:0}
}
});
var widgetCollection = Backbone.Collection.extend({
model: baseWidget
});
var widgetcol = new widgetCollection();
var baseView = Backbone.View.extend({
el: '.wraper',
render: function(pos = {left:0, top:0}) {
var widget = new baseWidget();
widgetcol.add(widget);
console.log(widgetcol.length);
widget.set({'position':pos})
var template = _.template($('#widget-template').html(), {widgetInfo: widget});
this.$el.html(template);
this.$el.offset({top:widget.get('position').top, left:widget.get('position').left})
$('.widget-wrap').draggable({
handle: ".widget-header",
stop: function (event, ui) {
widget.set({position: ui.position});
console.log(widget.get('position'));
}
});
}
});
BV = new baseView();
BV.render({left:0, top:0});
b = new baseView();
b.render({left:500, top:0});
any help would be greatly appreciated, also if I'm doing anything really strangely I would love advice on how to do it better.

When you are setting the el property in a view, youre binding the view to an existing element in the dom, limiting yourself to create only one widget. What you actually want to do is let the view generate the element markup and just append all the generated widgets to a certain parent.
You can do that by setting the tagName, className and id attributes in the view.
For example:
var baseView = Backbone.View.extend({
tagName: 'div',
className: '.wrapper'
...
});
That will generate a div with a class of wrapper that you can append to a container.
Later on, you define a click event to create a new widget each time:
$('button').click(function() {
var newView = new baseView();
newView.render();
$('.container').append(newView.el); // now 'el' correspond to the div.wrapper you just created
});
It is considered a good practice among backbone developers to return this from the view's render method. That way you could mix the last two lines like this:
$('.container').append(newView.render().el);
Also, instead if instanciating the collection before the view's definition, people tend to pass the collection as a property of the constructor parameter:
var collection = new widgetCollection();
BV = new baseView({ collection: collection });
Now you can reference the collection inside the view by simply this.collection.

Related

How do I prevent backbone remove() from removing "el" in a view?

I want remove a view before creating a new one. But my requirement is view.remove() should remove the view but not delete the el element. Having said this, I do not want to set tagName as it creates a new element which is unnecessary. Is there any way to remove a view from the memory leaving the el content cleared?
You can override Backbone's view remove method from within your abstract view:
remove: function() {
// this._removeElement();
this.$el.empty();
this.stopListening();
return this;
}
Default source code: http://backbonejs.org/docs/backbone.html#section-158
I have solved this before with a disposable launcher view.
make sure your html contains a (class or id) anchor for your disposable view:
<div class="content-container"></div>
then make a LauncherView:
var LauncherView = Backbone.View.extend({
initialize: function(options) {
this.render();
},
render: function() {
this.$el.html(this.template());
return this;
},
// inner views will be bound to ".launcher-container" via
// their .el property passed into the options hash.
template: _.template('<div class="launcher-container"></div>')
});
then instantiate your disposable launcher view:
app.currentLauncherView = new LauncherView({});
and append it to your DOM anchor:
$('.content-container').append(app.currentLauncherView.el);
then you can instantiate a view that will attach to the disposable launcher view:
app.throwAway1 = new DisposableView({el: '.launcher-container'});
And then when you want to destroy that view, you can do so with:
app.throwAway1.off();
app.throwAway1.remove();
app.currentLauncherView.remove();
Then you can put up a new view by instantiating a new LauncherView, attaching it to the DOM, and making your next view appear by binding it to '.launcher-container' .
app.currentLauncherView = new LauncherView({});
$('.content-container').append(app.currentLauncherView.el);
app.throwAway2 = new DisposableView({el: '.launcher-container'});

Backbone: Collection not rendered in View even though it's fetched

I know that there are a few questions around regarding this, but the answers are not very clear for me to implement. That's why I'm asking this question again so I can have a clear and simple answer.
I've always had trouble with Collection in Backbone, especially populating it with JSON data.
I can't seem to get the collection to render in the View, even though in firebug I can see that, it's being fetched from the server, but the screen is still empty.
Also, when I do a console.log('callers: ', this.callerList), it returns an object with models=[0]. But when I expand the object, models is full of data from the JSON file. What's going on with Backbone and it's confusing results?
Can someone please explain to me how to do it? I've been battling this for ages and I can't get my head around it.
Many Thanks
JS:
(function($, window) {
// model
var CallerModel = Backbone.Model.extend({});
// collection
var CallersList = Backbone.Collection.extend({
model: CallerModel,
url: 'js/json/callers.json'
});
// view
var CallerView = Backbone.View.extend({
el: '.caller-app',
template: _.template($('#callers-template').html()),
initialize: function() {
this.callerList = new CallersList();
this.callerList.fetch();
this.callerList.bind('reset', this.render);
console.log('caller: ', this.callerList);
},
render: function(e) {
console.log('RENDER');
_.each(this.collection.models, function(caller) {
this.$el.append(this.template(caller.toJSON()));
console.log('callerList: ', caller);
}, this);
return this;
}
});
// start
var callerView = new CallerView();
}(jQuery, window));
HTML:
<!-- wrapper -->
<div class="wrapper">
<h1>Missed Calls App</h1>
<div class="caller-app"></div>
</div>
<!-- wrapper -->
<!-- templates -->
<script type="text/template" id="callers-template">
<div class="caller">
<h2><%= title %> <%= name %> called</h2>
<h3>From <%= agency %></h3>
<p>When: <%= when %></p>
<p>Contact: <%= tel %></p>
<p>Says:"<%= message %>"</p>
</div>
</script>
<!-- templates -->
JSON:
[
{
"id": 1,
"title": "Mrs",
"name": "Mui",
"agency": "Ryuzanpaku Dojo",
"when": "evening",
"tel": "0207 123 45 67",
"message": "Check your availability"
},
{
"id": 2,
"title": "Mrs",
"name": "Shigure",
"agency": "Ryuzanpaku Dojo",
"when": "evening",
"tel": "0207 123 45 67",
"message": "Check your availability"
}
]
You haven't actaully assigned a collection to your CallerView, in addition when you iterate though the collection you should be using this.collection.models instead of this.model.models
For example when initializing you caller list
initialize: function() {
initialize: function() {
this.collection = new CallersList();
this.collection.fetch();
this.collection.bind('reset', this.render);
}
And when rendering
render: function(e) {
_.each(this.collection.models, function(caller) {
this.$el.append(this.template(caller.toJSON()));
}, this);
return this;
}
Here's a link to a jsbin
Some additional points
In general you want to decouple your code as much as possible. To this end it is probably better to declare and initialize your collection outside of your view and then pass it in. This also has the advantage of making your code more reusable, for example say you wanted to render a second list of calls (let say recent calls), you can now just create a second instance of your view passing in a collection and element.
For example
var missedCalls = new CallersList();
var callerView = new CallerView({collection : missedCalls, el: '#missedCalls' });
missedCalls.fetch();
var recentCalls = new CallerList(); //you probably want to use a different url
var recentCallersView = new CallerView({collection : recentCalls, el:'#recentCalls'});
recentCalls.fetch();
Another point worth mentioning, currently you are rendering all items in your collection for each fetch, including any that have been already rendered. You might want either empty your el before rendering or listen to the add event instead and render each item individually as it's added. In addition it's worth pointing out that fetch isn't really meant to be used to load data on page load, from the documentation
Note that fetch should not be used to populate collections on page
load — all models needed at load time should already be bootstrapped
in to place. fetch is intended for lazily-loading models for
interfaces that are not needed immediately: for example, documents
with collections of notes that may be toggled open and closed.

Backbone: remove ellipsis onclick

A bit of background
I'm trying to create a twitter like feed where the tweet/row will expand onclick, revealing more information.
The data is pulled from a JSON file (sent from the backend to the frontend).
I use backbone to render the data on the frontend.
Let's say my feed displays 10 rows, each row displays a few information then onclick the row/div expands to reveal more information.
The description field contains quite a lot of text therefore I'm applying a JavaScript ellipsis on it. I use Javascript ellipsis since the short description needs to be more than one line (don't think CSS ellipsis works for more than one line).
I created a plugin that will truncate the description text and onclick I want to remove the ellipsis and replace it by the full description (since the row will expand).
I created a plugin that will save the full description (before being truncated) into an array.
Issue
My idea was to compare the index of the row clicked (currentTarget) to the index of the rows saved (in the array) then replace the ellipsis text with the full description then expand the div with jQuery animate.
I'm not sure if there is a way to get an index from the backbone "click event" (in order to compare it to the index saved in the array)?
Feel free to let me know if there is a better way to approach this.
Thanks in advance
Here is my code:
Truncate & save original text functions
/**
* Plugins
*/
var arr = [];
$.fn.truncate = function(){
return this.each(function(index,element){
var elementText = $(element).text();
if(elementText.length > 165){
var truncated = elementText.trim().substring(0, 165).split(" ").slice(0, -1).join(" ") + "…";
}
$(element).text(truncated);
});
};
$.fn.getText = function(){
return this.each(function(index,element){
arr.push({
i: index,
v: $(element).text()
});
});
};
Backbone Model & Collections
/**
* Model
*/
var Task = Backbone.Model.extend();
/**
* Collections
*/
var RecentTasksList = Backbone.Collection.extend({
model: Task,
url: 'json/recentTasks.json'
});
Backbone Views
/**
* Views
*/
var RecentTasksView = Backbone.View.extend({
el: '.taskList',
template: _.template($('#recentTasksTemplate').html()),
render: function(){
_.each(this.model.models, function(data){
this.$el.append(this.template(data.toJSON()));
}, this);
$('.description').getText();
$('.description').truncate();
return this;
}
});
var FullTaskView = Backbone.View.extend({
el: '.taskContainer',
events: {
'click .task': 'showFullDetails'
},
showFullDetails: function(e){
var eTarget = $(e.currentTarget);
var $desc = $('.description');
if(eTarget.hasClass('expanded')){
eTarget.animate({
'height': '80px'
},
function(){
eTarget.removeClass('expanded');
});
}
else{
console.log($(eTarget).find($desc).html());
eTarget.animate({
//doesn't work lesser IE 8
'height': eTarget[0].scrollHeight
},
function(){
eTarget.addClass('expanded');
});
}
}
});
var AppView = Backbone.View.extend({
el: 'body',
initialize: function(){
//Recent Tasks
var recentTasksList = new RecentTasksList();
var recentTasksView = new RecentTasksView({
model: recentTasksList
});
recentTasksList.bind('reset', function(){
recentTasksView.render();
});
recentTasksList.fetch();
//Full Task Details
var fullTaskView = new FullTaskView();
}
});
var appView = new AppView();
Underscore template
<script id="recentTasksTemplate" type="text/template">
<div class="task clearfix">
<div class="image">
<img src="<%= image %>" />
</div>
<div class="details">
<h3 class="title"><%= title %></h3>
<div class="description">
<%= description %>
</div>
</div>
<div>
</script>
HTML
<div class="taskContainer">
<div class="taskList"></div>
</div>
EDIT
One last question. I added a tab to my page (similar call to action). Same type of information will be display onclick (I'm using the same template). For instance I now have RecentTask and PopularTask.
I created a view for the tabs containing click events. Do I need to instanciate the model & view & fetch the data each time or can I reuse the ones already initialized?
I created a new view for a second tab. Grabbing JSON file from the server:
var PopularTasksList = Backbone.Collection.extend({
model: Task,
url: 'json/popularTasks.json'
});
var PopularTasksView = Backbone.View.extend({
el: '.taskList',
render: function(){
$('.taskList').empty();
_.each(this.model.models, function(model){
var taskView = new TaskView({model: model});
this.$el.append(taskView.render().el);
}, this);
return this;
}
});
Then I created a tab view that will show the correct Tasks onclick.
var TabsView = Backbone.View.extend({
el: 'body',
events:{
'click .tabRecent': 'fetchDataRecentTasks',
'click .tabPopular': 'fetchDataPopularTasks'
},
fetchDataRecentTasks: function(){
var recentTasksList = new RecentTasksList();
var recentTasksView = new RecentTasksView({
model: recentTasksList
});
recentTasksList.bind('reset', function(){
recentTasksView.render();
});
recentTasksList.fetch();
},
fetchDataPopularTasks: function(){
var popularTasksList = new PopularTasksList();
var popularTasksView = new PopularTasksView({
model: popularTasksList
});
popularTasksList.bind('reset', function(){
popularTasksView.render();
});
popularTasksList.fetch();
}
});
I think you should create a new view for an individual task. Then in that view, you can handle the click, so you have access to the task model, and also access to the DOM of that view very easily.
Then you can get rid of your FullTaskView, and the jQuery plugins.
/**
* Model
*/
var Task = Backbone.Model.extend({
getShortDescription: function(){
var desc = this.get('description');
if(desc.length > 165){
return desc.trim().substring(0, 165).split(" ").slice(0, -1).join(" ") + "…";
}
return desc;
}
});
Add new TaskView, and change RecentTasksView to create/render them.
/**
* Views
*/
var TaskView = Backbone.View.extend({
template: _.template($('#recentTasksTemplate').html()),
events: {
'click': 'showFullDetails'
},
render: function(){
// pass the model json, plus the short description to the template
this.$el.html(this.template({
data: this.model.toJSON(),
shortDesc: this.model.getShortDescription()
}));
return this;
},
showFullDetails: function(){
// change text, show/hide, animate here
// In the view, this.$() will only match elements within this view.
// if expand...
this.$('.description').html(this.model.get('description'));
// if hide...
this.$('.description').html(this.model.getShortDescription());
}
});
var RecentTasksView = Backbone.View.extend({
el: '.taskList',
render: function(){
_.each(this.model.models, function(model){
// create a view for each task, render and append it
var taskView = new TaskView({model: model});
this.$el.append(taskView.render().el);
}, this);
return this;
}
});
Change template to use new data passed to it.
// access the model stuff with data.title, etc.
<script id="recentTasksTemplate" type="text/template">
<div class="task clearfix">
<div class="image">
<img src="<%= data.image %>" />
</div>
<div class="details">
<h3 class="title"><%= data.title %></h3>
<div class="description">
<%= shortDesc %>
</div>
</div>
<div>
</script>
EDIT:
A Backbone view is meant to manage a DOM element, so it is just a good idea to have each task be its own view instance. This makes it easier to do the expanding and changing the text based on the click. Also it is a best practice to not have code outside the view changing things inside its DOM element, so it is good to do that manipulation inside each task view.
This is similar to a TodoView in the Todo sample:
http://backbonejs.org/docs/todos.html
http://backbonejs.org/examples/todos/index.html
You can pass the template function any javascript object (even an object with functions, not just properties). Since you want to display some data that is not technically part of the model, passing that data object is just a way to get the stuff you need into the template.

Backbone events not firing on new element added to DOM

I'm using a combination of handlebars and Backbone. I have one "container" view which has an array to hold child views. Whenever I add a new view, click events are not being bound.
My Post View:
Post.View = Backbone.View.extend({
CommentViews: {},
events: {
"click .likePost": "likePost",
"click .dislikePost": "dislikePost",
"click .addComment button": "addComment"
},
render: function() {
this.model.set("likeCount", this.model.get("likes").length);
this.model.set("dislikeCount", this.model.get("dislikes").length);
this.$('.like-count').html(this.model.get("likeCount") + " likes");
this.$('.dislike-count').html(this.model.get("dislikeCount") + " dislikes");
return this;
}, ...
My callback code in the "container" view which creates a new backbone view, attaches it to a handlebars template and shows it on the page:
success: _.bind(function(data,status,xhr) {
$(this.el).find("#appendedInputButton").val('');
var newPost = new Post.Model(data);
var newPostView = new Post.View({model: newPost, el: "#wall-post-" + newPost.id});
var source = $("#post-template").html();
var template = Handlebars.compile(source);
var html = template(newPost.toJSON());
this.$('#posts').append(html);
newPostView.render();
this.PostViews[newPost.id] = newPostView;
}, this), ...
Not sure what's going on, but this sort of code is run initially to set up the page (sans handlebars since the html is rendered server-side) and all events work fine. If I reload the page, I can like/dislike a post as well.
What am I missing?
I dont see you appending newPostView.render().el to dom .Or am i missing somehting?
Assuming the "#post-template" contains the "likePost" button. The newPostView is never added to the DOM.
Adding el to the new Post.View makes backbone search the DOM (and the element won't exist yet)
4 lines later a HTML string is added to the DOM (assuming the this.el is already in the DOM)
If you create the Post.View after the append(html) the element can be found and events would be fireing.
But the natural Backbone way would be to render the HTML string inside the Post.View render function, append the result to it's el and append that el to the #posts element.
success: function (data) {
var view = new Post.View({model: new Post.Model(data)});
this.$('#posts').append(view.render().el);
this.PostViews[data.id] = view;
}

Setting id and className dynamically in Backbone.js views

I am in process of learning and using Backbone.js.
I have an Item model and a corresponding Item view.
Each model instance has item_class and item_id attributes, that I want to be reflected in as the 'id' and 'class' attributes of the corresponding view.
What's the correct way to achieve this ?
Example:
var ItemModel = Backbone.Model.extend({
});
var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});
var ItemView = Backbone.View.extend({
});
How should I implement the view so that the the views 'el's will translate to:
<div id="id1" class="nice"></div>
<div id="id2" class="sad"> </div>
In most examples I have seen, the view's el serves as a meaningless wrapper element inside which one has to manually write the 'semantic' code.
var ItemView = Backbone.View.extend({
tagName: "div", // I know it's the default...
render: function() {
$(this.el).html("<div id="id1" class="nice"> Some stuff </div>");
}
});
So when rendered, one gets
<div> <!-- el wrapper -->
<div id="id1" class="nice"> Some stuff </div>
</div>
But this seems like a waste - why have the external div ? I want the el to translate directly into the internal div!
Summary: dynamically set view attributes with model data
http://jsfiddle.net/5wd0ma8b/
// View class with `attributes` method
var View = Backbone.View.extend( {
attributes : function () {
// Return model data
return {
class : this.model.get( 'item_class' ),
id : this.model.get( 'item_id' )
};
}
// attributes
} );
// Pass model to view constructor
var item = new View( {
model : new Backbone.Model( {
item_class : "nice",
item_id : "id1"
} )
} );
This example assumes that you're allowing Backbone to generate a DOM element for you.
The attributes method is called after the properties passed to the view constructor are set (in this case, model), allowing you to dynamically set the attributes with the model data before Backbone creates el.
In contrast to some of the other answers: doesn't hard-code attribute values in the view class, dynamically sets them from model data; doesn't wait until render() to set attr vals; doesn't repeatedly set attr vals in every call to render(); doesn't unnecessarily manually set attr vals on DOM element.
Note that if setting the class when calling Backbone.View.extend or a view constructor (e.g. new Backbone.View), you have to use the DOM property name, className, but if setting it via the attributes hash / method (as in this example) you have to use the attribute name, class.
As of Backbone 0.9.9:
When declaring a View...el, tagName, id and className may now be defined as functions, if you want their values to be determined at runtime.
I mention this in case there's a situation where that would be useful as an alternative to using an attributes method as illustrated.
Using an existing element
If you're using an existing element (e.g. passing el to the view constructor)...
var item = new View( { el : some_el } );
...then attributes won't be applied to the element. If the desired attributes aren't already set on the element, or you don't want to duplicate that data in your view class and another location, then you may want to add an initialize method to your view constructor that applies attributes to el. Something like this (using jQuery.attr):
View.prototype.initialize = function ( options ) {
this.$el.attr( _.result( this, 'attributes' ) );
};
Usage of el, rendering, avoiding the wrapper
In most examples I have seen, the view's el serves as a meaningless wrapper element inside which one has to manually write the 'semantic' code.
There's no reason view.el needs to be "a meaningless wrapper element". In fact, that would often break the DOM structure. If a view class represents a <li> element for example, it needs to be rendered as an <li> -- rendering it as a <div> or any other element would break the content model. You'll likely want to focus on correctly setting up your view's element (using properties like tagName, className, and id) and then rendering its content thereafter.
The options for how to have your Backbone view objects interact with the DOM are wide open. There are 2 basic initial scenarios:
You can attach an existing DOM element to a Backbone view.
You can allow Backbone to create a new element that is disconnected from the document, then somehow insert it into the document.
There are various ways you can generate the content for the element (set a literal string, as in your example; use a templating library like Mustache, Handlebars, etc.). How you should use the el property of the view depends what you're doing.
Existing element
Your rendering example suggests that you have an existing element that you're assigning to the view, although you don't show instantiation of the views. If that's the case, and the element is already in the document, then you may want to do something like this (update the content of el, but don't alter el itself):
render : function () {
this.$el.html( "Some stuff" );
}
http://jsfiddle.net/vQMa2/1/
Generated element
Let's say you don't have an existing element and you allow Backbone to generate one for you. You may want to do something like this (but it's likely better to architect things so that your view isn't responsible for knowing about anything outside itself):
render : function () {
this.$el.html( "Some stuff" );
$( "#some-container" ).append( this.el );
}
http://jsfiddle.net/vQMa2/
Templates
In my case, I'm using templates, e.g.:
<div class="player" id="{{id}}">
<input name="name" value="{{name}}" />
<input name="score" value="{{score}}" />
</div>
<!-- .player -->
The template represents the complete view. In other words, there will be no wrapper around the template -- div.player will be the root or outermost element of my view.
My player class will look something like this (with very simplified example of render()):
Backbone.View.extend( {
tagName : 'div',
className : 'player',
attributes : function () {
return {
id : "player-" + this.model.cid
};
},
// attributes
render : function {
var rendered_template = $( ... );
// Note that since the top level element in my template (and therefore
// in `rendered_template`) represents the same element as `this.el`, I'm
// extracting the content of `rendered_template`'s top level element and
// replacing the content of `this.el` with that.
this.$el.empty().append( rendered_template.children() );
}
} );
In your view just do something like this
var ItemView = Backbone.View.extend({
tagName: "div", // I know it's the default...
render: function() {
$(this.el).attr('id', 'id1').addClass('nice').html('Some Stuff');
}
});
You can set the properties className and id on the root element:
http://documentcloud.github.com/backbone/#View-extend
var ItemView = Backbone.View.extend({
tagName: "div", // I know it's the default...
className : 'nice',
id : 'id1',
render: function() {
$(this.el).html("Some stuff");
}
});
EDIT Included example of setting id based on constructor parameters
If the views are constructed as mentioned:
var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});
Then the values could be set this way:
// ...
className: function(){
return this.options.item_class;
},
id: function(){
return this.options.item_id;
}
// ...
I know it's an old question, but added for reference. This seems to be easier in new backbone versions. In Backbone 1.1 the id and className properties are evaluated in the function ensureElement (see from source) using underscore _.result meaning if className or id is a function, it will be called, otherwise its value will be used.
So you could give className directly in the constructor, give another parameter that would be used in the className, etc... Plenty of options
so this should work
var item1 = new ItemModel({item_class: "nice", item_id: "id1"});
var item2 = new ItemModel({item_class: "sad", item_id: "id2"});
var ItemView = Backbone.View.extend({
id: function() { return this.model.get('item_id'); },
className: function() { return this.model.get('item_class'); }
});
The other examples are not showing how to actually grab the data from the model. To dynamically add id and class from the model's data:
var ItemView = Backbone.View.extend({
tagName: "div",
render: function() {
this.id = this.model.get('item_id');
this.class = this.model.get('item_class');
$(this.el).attr('id',this.id).addClass(this.class).html('Some Stuff');
}
});
You need to remove tagName and declare an el.
'tagName' signifies that you want backbone to create an element. If the element already exists in the DOM, you can specify an el like:
el: $('#emotions'),
and later:
render: function() {
$(this.el).append(this.model.toJSON());
}
Try to assign the values in initialize method this will directly assign id and class to the div attribute dynamically.
var ItemView = Backbone.View.extend( {
tagName : "div",
id : '',
class : '',
initialize : function( options ) {
if ( ! _.isUndefined( options ) ) {
this.id = options.item_id;
this.class= options.item_class;
}
},
render : function() {
$( this.el ).html( this.template( "stuff goes here" ) );
}
} );
Here's a minimal way to change the class of the view's element dynamically via a model and update it on model changes.
var VMenuTabItem = Backbone.View.extend({
tagName: 'li',
events: {
'click': 'onClick'
},
initialize: function(options) {
// auto render on change of the class.
// Useful if parent view changes this model (e.g. via a collection)
this.listenTo(this.model, 'change:active', this.render);
},
render: function() {
// toggle a class only if the attribute is set.
this.$el.toggleClass('active', Boolean(this.model.get('active')));
this.$el.toggleClass('empty', Boolean(this.model.get('empty')));
return this;
},
onClicked: function(e) {
if (!this.model.get('empty')) {
// optional: notify our parents of the click
this.model.trigger('tab:click', this.model);
// then update the model, which triggers a render.
this.model.set({ active: true });
}
}
});

Categories

Resources