I am building a view at the moment, and when I click on a .organisation link I want to fire my edit event, however on clicking this element, nothing is fired, and I cannot understand why.
Here is the code that builds my view,
App.Views.groupsView = Backbone.View.extend({
el: '.app',
template: _.template( $('#tpl-groups-base').html() ),
events: {
},
initialize: function(options) {
this.render();
},
render: function() {
this.$el.html( this.template() );
var orgTab = new App.Views.OrganisationsTab({
collection : new App.Collections.Organisations
});
},
});
App.Views.OrganisationsTab = Backbone.View.extend({
el : '#organisations',
initialize: function() {
App.Collections.OrganisationCollection = this.collection;
this.collection.fetch();
this.collection.on('sync', this.render, this);
},
render: function() {
this.addAll();
return this;
},
addAll: function() {
App.Collections.OrganisationCollection.each(this.addOne, this);
},
addOne: function(organisation) {
var view = new App.Views.OrganisationView({
model : organisation
});
this.$el.append( view.render().el );
}
});
App.Views.OrganisationView = Backbone.View.extend({
tagName: 'a',
className:'group group--panel col-sm-3 organisation',
template : _.template( $('#tpl-single-group').html() ),
events: {
"click body" : "edit",
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destory', this.remove);
},
render: function() {
this.$el.html( this.template({
group: this.model.toJSON()
}));
return this;
},
edit: function(e) {
e.preventDefault();
console.log(this.model);
}
});
Why would I not be able to click on the organisation a that is created in the final view, and trigger my edit function?
If you want to attach a click event listener to a child element of a view's el
events: {
"click .organisation" : "edit",
},
If you want to attach it to the el
events: {
"click" : "edit",
},
Related
need help, can't understand how to attach each View of the model to each already existing DIV in DOM ( have and div.container with div.widget array ).
// Model
V.Models.Shortcode = Backbone.Model.extend({});
// Shortcodes Collection Init
V.Collections.Shortcodes = Backbone.Collection.extend({
model: V.Models.Shortcode,
});
When Iframe load, push storage from server to collection:
$('#preview').on('load', function() {
var ShortcodesCollection = new V.Collections.Shortcodes( Vision.ShortcodeStorage );
var Preview = new V.Views.Preview({
collection: ShortcodesCollection,
el: $('.container')
});
Preview.render();
});
Render Preview with collection:
// Collection View in iframe
V.Views.Preview = Backbone.View.extend({
initialize: function() {
this.collection.on('add', this.addOne, this);
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(ad) {
var shortcodeView = new V.Views.Shortcode({ model: ad });
shortcodeView.render();
}
});
View for each Model:
// Shortcode View
V.Views.Shortcode = Backbone.View.extend({
events: {
'click .widget' : 'SomeActionOnView'
},
render: function(){
//console.log(this.el);
//console.log(this.model.toJSON());
},
SomeActionOnView: function(){
console.log(this);
}
});
Question is, how to attach V.Views.Shortcode to each div with "widget" class to bind events. Thanks!
Can you please try this?
V.Views.Shortcode = Backbone.View.extend({
events: {
'click .widget' : 'SomeActionOnView'
},
render: function(){
//code here to write stuff of this.$el
$("div.widget").append(this.$el);
},
SomeActionOnView: function(){
console.log(this);
}
});
I've just started using backbone.js to create a spa. I'm trying to make a books collection listen to model change but nothing happens. This is the model and collection:
var Book = Backbone.Model.extend({
idAttribute: 'id',
defaults: {
id: null,
title: '',
author: '',
completed: false
}
});
var BooksCollection = Backbone.Collection.extend({
model: Book,
url: 'books',
initialize: function() {
this.listenTo(this.model, 'change', this.debug);
},
debug: function () {
console.log('something happened !!!');
}
});
This is the view:
var BookView = Backbone.View.extend({
template: _.template($('#viewBook').html()),
events: {
"click #save": "bookSave"
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
},
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
bookSave: function() {
this.model.set({author: "nobody"});
}
});
When I click on the #save button the author is changed but nothing is logged. Where am I wrong?
Events from models automatically bubbles up to the collection, so you can listen to model changes directly like this:
// in the collection
initialize: function() {
this.listenTo(this, 'change', this.debug);
},
In my web application, I have a model that is mixture of normal standard attributes (strings, booleans, etc) and the collections. In the application a user can create a group and add a projects to that group. The group is collection with the model. I can add and remove projects to the group absolutely fine, in so much that i can see the projects being added or removed in my logging, and also in the database.
To add projects to a group I open a modal and clicking a button in that model triggers model.save() and this triggers a change event. However if I add user and then remove without reloading the page, the change is not fired, why would this be? Here is the modal view,
Modal View
Views.OrganisastionEditView = Backbone.View.extend({
className : 'modal-body',
template: _.template( $('#tpl-edit-organisation').html() ),
events: {
"click .js-add-member" : "addMember",
"click .js-add-client" : "addClient",
"click .js-add-project" : "addProject",
"click .js-add-team" : "addTeam",
"click .search-results a" : "selectSearchResult",
"click .js-remove-pill" : "removeAttribute",
"submit .edit-organisation" : "saveOrganisation",
"click .js-remove-delete" : "deleteOrganisation",
"click .js-make-admin" : "changeAdmin"
},
initialize: function() {
//this.listenTo(this.model, 'change', this.snyc);
this.listenTo(this.model.get('members'), 'change', this.changeMember);
this.render();
},
render: function() {
var self = this;
// this.model.initialize();
$('#myModal').on('hidden.bs.modal', function () {
self.remove();
Pops.Routes.Application.navigate('/groups', { trigger: false } );
});
this.$el.html( this.template({
organisation: this.model.toJSON()
})).insertAfter('.modal-header');
var organisationProjectsView = new Views.GroupsProjectsViews({
collection: this.model.get('projects')
});
var organisationClientsView = new Pops.Views.GroupsClientsViews({
collection: this.model.get('clients')
});
var organisationMembersView = new Views.GroupsMembersAdminViews({
collection: this.model.get('members')
});
var organisationTeamsView = new Views.GroupsTeamsViews({
collection: this.model.get('teams')
});
$("#myModal").modal();
},
deleteOrganisation: function(e) {
e.preventDefault();
this.model.destroy();
$("#myModal").modal('hide');
this.remove();
},
removeAttribute: function(e) {
e.preventDefault();
var element = $(e.currentTarget);
switch(element.data('type')) {
case "project":
console.log(this.model.get('projects'));
this.model.get('projects').remove(element.data('id'));
element.parents('.avatar-pill').remove();
break;
case "client":
this.model.get('clients').remove(element.data('id'));
element.parents('.avatar-pill').remove();
break;
}
},
addMember: function(e) {
e.preventDefault();
var element = $(e.currentTarget);
this.$('.search').parent().children().show();
this.$('.search').first().remove();
//element.parent().children().hide();
var search = new Views.SearchView({
collection: new Collections.Users,
type : "users",
merge: false
});
element.parent().append(search.render().el);
},
addProject: function(e) {
e.preventDefault();
var element = $(e.currentTarget);
this.$('.search').parent().children().show();
this.$('.search').first().remove();
//element.parent().children().hide();
var search = new Views.SearchView({
collection: new Collections.Projects,
type : "projects",
merge: false
});
element.parent().append(search.render().el);
},
selectSearchResult: function(e) {
e.preventDefault();
var element = $(e.currentTarget),
self = this;
switch( element.data('type')) {
case "project":
var project = new Models.Project({ id: element.data('id')});
project.fetch({
success: function() {
self.model.get('projects').add(project);
console.log(self.model.get('projects'));
var model = self.model;
self.$('.search').hide();
self.$('button').show();
var projectsDetails = new Views.ProjectNamePillView({
model : project
});
self.$('.search').parent().append( projectsDetails.render().el );
self.$('.search').remove();
}
});
break;
}
},
saveOrganisation: function(e) {
e.preventDefault();
var element = $(e.currentTarget);
var data = element.serializeJSON();
this.model.set(data);
this.model.save();
},
});
Single Model View
Views.OrganisationView = Backbone.View.extend({
tagName: 'div',
className:'group group--panel col-sm-3',
template : _.template( $('#tpl-single-group').html() ),
events: {
"click a[data-type=organisation], button[data-type=organisation]" : "edit",
"click .js-delete-group" : "removeOrganisation",
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
//this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.removeView);
},
render: function() {
console.log('getting fired');
this.$el.html( this.template({
group: this.model.toJSON()
}));
return this;
},
removeView: function() {
this.remove();
},
removeOrganisation: function(e) {
this.model.destory();
this.remove();
},
edit: function(e) {
e.preventDefault();
Routes.Application.navigate('/organisation/edit/' + this.model.get('id'), { trigger: false } );
var editClient = new Views.OrganisastionEditView({
model: this.model
});
}
});
Change events are triggered on the model itself not its attributes. You can however listen to a particular attribute change by using the change:[attribute] event naming format.
In your example you would change:
this.listenTo(this.model.get('members'), 'change', this.changeMember);
To look like:
this.listenTo(this.model, 'change:members', this.changeMember);
Check the Backbonejs event docs.
I'm trying to make the View dynamic as when someone touches the item, but using the '<%= myClassName %>' inside the View doesn't work. I can't use this technique inside the HTML file as it would draw another element and that's not the idea. Also I have set a template but it has nothing on it. I just did that to relate jQuery Mobile into a data-role="content" and render the view inside the content. Any ideas?
Here is what I have:
app.js
var TodoItem = Backbone.Model.extend({
toggleStatus: function(){
if(this.get('status') === 'incomplete'){
this.set({'status': 'complete'});
} else {
this.set({'status': 'incomplete'});
}
this.save();
// PUT /TODOS/1
}
});
var TodoItems = Backbone.Collection.extend({
model: TodoItem,
localStorage: new Backbone.LocalStorage("button"),
initialize: function () {
this.on('remove', this.hideModel, this);
},
hideModel: function (model) {
model.trigger('hide');
}
});
var TodosView = Backbone.View.extend({
initialize: function () {
this.collection.on('add', this.addOne, this);
},
addOne: function (todoItem) {
var todoView = new TodoView({ model: todoItem });
this.$el.append(todoView.render().el);
},
addAll: function () {
this.collection.forEach(this.addOne, this);
},
render: function() {
this.collection.forEach(this.addOne, this);
this.addAll;
return this;
}
});
var TodoView = Backbone.View.extend({
tagName: 'span',
// THIS IS THE MAIN PROBLEM
className: '<%= status %>',
// END COMMENT
template: _.template( $('#personTemplate').html() ),
events: {
"touchstart": "toggleStatus",
"touchend": "toggleStatus"
},
toggleStatus: function () {
this.model.toggleStatus();
},
remove: function(){
this.$el.remove();
},
initialize: function(){
this.render();
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
this.model.on('hide', this.remove, this);
},
render: function () {
var attributes = this.model.toJSON();
this.$el.html(this.template(attributes));
return this;
}
});
var todoItems = new TodoItems([
{
description: 'Jeffrey Way',
status: "incomplete",
id: 1
},
{
description: 'John Doe',
status: "incomplete",
id: 2
},
{
description: 'Sally Doe',
status: "incomplete",
id: 3
}
]);
var todosView = new TodosView({
el: $('#elem'),
collection: todoItems
});
todosView.render().el
You can do
this.$el.addClass(this.model.get('status'))
inside the view's render method.
Trying to use a template value in the view code doesn't make any sense; those properties are set when the object is parsed, so how would it know which object to fetch the status from?
The best thing todo, if i understand correctly, is to listen to the change:status in your view, and adding/removing a class to your view according to the value of status.
in your initialize of TodoView:
this.listenTo(this.model, 'change:status', this.changeStatus);
in your TodoView declare:
changeStatus : function(model, value, options)
{
// add a class or toggle it or change something in the view..
}
Solution 1
var TodoView = Backbone.View.extend({
tagName: 'span',
...})
var x=new TodoView ({className:'sample'});
Solution 2
Use a template!
var TodoView = Backbone.View.extend({
template="<span class=<%-className%>>Some Stuff Goes Here</span>",
...
render:function(){
var $ele=$(_.template(this.template,{className:'sample'}));
this.$el.replaceWith($ele);
this.$el=$ele;
this.delegateEvents(); //inbuilt-method, to re-bind all event handlers
});
I make a simple todo app:
var Todo = Backbone.Model.extend({
});
var Todos = Backbone.Collection.extend({
model: Todo
});
var todos = new Todos();
var ItemView = Backbone.View.extend({
tagName: "li",
template: _.template($("#item-template").html()),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function () {
this.listenTo(todos, 'remove', this.remove);
},
events: {
"click .delete": "clear"
},
clear: function () {
todos.remove(this.model);
}
});
var AppView = Backbone.View.extend({
el: $("body"),
initialize: function () {
this.listenTo(todos, 'add', this.addOne);
},
addOne: function(todo) {
var view = new ItemView({
model: todo
});
this.$("#list").append(view.render().el);
},
events: {
"click #create": "create"
},
create: function () {
var model = new Todo({
title: this.$("#input").val()
});
todos.add(model);
}
})
var app = new AppView();
and DEMO online is here: http://jsfiddle.net/JPL94/1/
I can add item correctly, but when I want delete some item, all of them been removed;
I found it related to the bind event in ItemView, when I click one delete button, all of them are triggered.
But how can I solve this problem?
You are listening to remove events from the collection, and if my memory serves me right a collection will dispatch a remove event whenever a model is removed, so when you remove a model from the collection, all the views will see the event.
I changed your initialize in the view to
initialize: function () {
this.listenTo(this.model, 'remove', this.remove);
},
And it seems to work.
http://jsfiddle.net/JPL94/5/