Backbone.js firing event from a view inside a view - javascript

Have som trouble getting the code below to work.
I am trying to fire an event from a rendered sub view that has its own events object.
Is it possible to do this in an easy way?
var SubView = Backbone.View.extend({
events: {
'click .subview-item a': 'test'
},
el: '#subview',
test: function() {
console.log('please print this...');
},
initialize: function() {
this.template = '<div class="subview-item">Clickable Subview</div>'
},
render: function(){
$(this.el).html(_.template(this.template));
return this;
}
});
var MainView = Backbone.View.extend({
el: $('#content'),
initialize: function(){
this.template = '<h1>Hello</h1><div id="subview"></div>';
this.subView = new SubView();
},
render: function(){
$(this.el).html(_.template(this.template));
this.subView.render();
return this;
}
});
var mainView = new MainView({});
mainView.render();
Any ideas??

When you create your subView inside MainView's initialize, the #subview element does not yet exist in your DOM, since you have not yet rendered MainView. So a new <div> is created outside the DOM. You need to first render MainView before creating the SubView. You could do that inside the MainView's render() but the following is simpler I think:
var SubView = Backbone.View.extend({
events: {
'click .subview-item a': 'test'
},
el: '#subview',
test: function() {
console.log('please print this...');
},
initialize: function() {
this.template = _.template('<div class="subview-item">Clickable Subview</div>');
},
render: function() {
this.$el.html(this.template);
return this;
}
});
var MainView = Backbone.View.extend({
el: $('#content'),
initialize: function() {
this.template = _.template('<h1>Hello</h1><div id="subview"></div>');
},
render: function() {
this.$el.html(this.template);
return this;
}
});
var mainView = new MainView();
mainView.render();
var subView = new SubView();
subView.render();
Also took the liberty to correct a few things like using this.$el and creating the template on initialize() rather than recompiling on every render().

Related

Attach View of Model to existing DIV in Backbone

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);
}
});

Backbone: Move a model to another Collection

This is my first time using backbone, so I'm pretty confused about everything. I'm trying to make a todo list. Once I click "finished" on the todo, I want it to append to the "Completed" list.
I've been following this tutorial, and I tried to replicate the code(I tried to create a new completedTodo view and stuff like that), and I tried to do when clicking "finished" it would delete the $el, and I would add to the completedTodos. I think the problem here is even if it's added, it's not doing anything.
done: function() {
var completed = new CompletedTodo({
completedTask: this.$('.task').html(),
completedPriority: this.$('.priority').html()
});
completedTodos.add(completed);
this.model.destroy();
},
I put in a debugger there to see if it actually added to the collection, and when i did completedTodos.toJSON();, it does give me back the new thing I just added.
However, it does not append to my collection list.
Here is my whole entire script file, in case I named anything wrong.
var Todo = Backbone.Model.extend({
defaults: {
task: '',
priority: ''
}
});
var CompletedTodo = Backbone.Model.extend({
defaults: {
completedTask: '',
completedPriority: ''
}
});
var Todos = Backbone.Collection.extend({});
var todos = new Todos();
var CompletedTodos = Backbone.Collection.extend({});
var completedTodos = new CompletedTodos();
//Backbone view for one todo
var TodoView = Backbone.View.extend({
model: new Todo(),
tagName: 'tr',
initialize: function() {
this.template = _.template($('.todos-list-template').html());
},
events: {
'click .finished-todo': 'done',
'click .delete-todo' : 'delete'
},
done: function() {
var completed = new CompletedTodo({
completedTask: this.$('.task').html(),
completedPriority: this.$('.priority').html()
});
completedTodos.add(completed);
this.model.destroy();
},
delete: function() {
this.model.destroy();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
//Backbone view for all todos
var TodosView = Backbone.View.extend({
model: todos,
el: $('.todos-list'),
initialize: function() {
this.model.on('add', this.render, this);
this.model.on('remove', this.render, this);
},
render: function() {
var self = this;
this.$el.html('');
_.each(this.model.toArray(), function(todo) {
self.$el.append((new TodoView({model: todo})).render().$el);
});
return this;
}
});
//View for one Completed Todo
var CompletedTodoView = Backbone.View.extend({
model: new CompletedTodo(),
tagName: 'tr',
initialize: function() {
this.template = _.template($('.completed-todos-template').html());
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
//View for all Completed Todos
var CompletedTodosView = Backbone.View.extend({
model: completedTodos,
el: $('.completed-todos-list'),
initialize: function() {
this.model.on('add', this.render, this);
},
render: function() {
var self = this;
this.$el.html('');
_.each(this.model.toArray(), function(completedTodo) {
self.$el.append((new CompletedTodoView({model: completedTodo})).render().$el);
});
return this;
}
});
var todosView = new TodosView();
$(document).ready(function() {
$('.add-todo').on('click', function() {
var todo = new Todo({
task: $('.task-input').val(),
priority: $('.priority-input').val()
});
$('.task-input').val('');
$('.priority-input').val('');
todos.add(todo);
});
});
After this, I also have to figure out how to use Parse to make it persist to the database. I figured I'd get everything working in backbone first, and then try to do put in the database. I'm also suppose to use node/express, so would that help? I'm pretty much a Ruby on Rails kind of person, so I don't really know any of these javascript framework type of stuff.
Thanks for your help!
Alright,
It was just because I didn't initialize the view.
var completedTodosView = new CompletedTodosView();
This fixed it.

Backbone.js: Using Embeded Javascript inside the View

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
});

Backbone view not getting the JSON objects

Making a Rails app with Backbone on the frontend. There are two Backbone models, Publications and Articles. I am having issues rendering one of my Backbone views. Here is the code that is giving me an issue:
SimpleGoogleReader.Views.PublicationsIndex = Backbone.View.extend({
template: JST['publications/index'],
el: '#publication',
events:{
'click #new_feed': 'createFeed'
},
initialize: function() {
this.listenTo(this.model, "sync", this.render);
},
render: function(){
this.$el.html( this.template({publications: this.model.toJSON()}) );
return this;
},
createFeed: function(e){
e.preventDefault();
var feed_url = $('#new_feed_name').val();
// var that = this;
this.model.create(
{url: feed_url},
{ success: function(data){
$.post('/articles/force_update', {url: feed_url, publication_id: data.id}, function(data){
});
}
}
);
}
});
I know the render function is getting called and when I console.log(this.model.toJSON()) it does not return the JSON objects. However the render function works just fine within the Articles View:
SimpleGoogleReader.Views.ArticlesIndex = Backbone.View.extend({
template: JST['articles/index'],
el: '#article',
initialize: function() {
this.listenTo(this.model, "sync", this.render);
},
render: function(){
console.log(this.model.toJSON());
this.$el.html( this.template({articles: this.model.toJSON()}) );
return this;
}
});
** Edit ** here is my router where I instantiate the views:
SimpleGoogleReader.Routers.Publications = Backbone.Router.extend({
routes: {
'': 'home',
'all_articles': 'get_all_articles',
'publications/:id': 'articles_by_id',
'publications/:id/delete': 'delete_publication'
},
home: function(){
var publications = new SimpleGoogleReader.Collections.Publications();
var articles = new SimpleGoogleReader.Collections.Articles();
var pubIndex = new SimpleGoogleReader.Views.PublicationsIndex({model: publications});
var artIndex = new SimpleGoogleReader.Views.ArticlesIndex({model: articles});
articles.listenTo(publications, "sync", function(){
articles.fetch( {success: function(){}} );
});
publications.fetch();
},
Any ideas on where the discrepancy may lie?

How to filter Backbone.js Collection and Rerender App View?

Is is a total Backbone.js noob question. I am working off of the ToDo Backbone.js example trying to build out a fairly simple single app interface. While the todo project is more about user input, this app is more about filtering the data based on the user options (click events).
I am completely new to Backbone.js and Mongoose and have been unable to find a good example of what I am trying to do. I have been able to get my api to pull the data from the MongoDB collection and drop it into the Backbone.js collection which renders it in the app. What I cannot for the life of me figure out how to do is filter that data and re-render the app view. I am trying to filter by the "type" field in the document.
Here is my script:
(I am totally aware of some major refactoring needed, I am just rapid prototyping a concept.)
$(function() {
window.Job = Backbone.Model.extend({
idAttribute: "_id",
defaults: function() {
return {
attachments: false
}
}
});
window.JobsList = Backbone.Collection.extend({
model: Job,
url: '/api/jobs',
leads: function() {
return this.filter(function(job){ return job.get('type') == "Lead"; });
}
});
window.Jobs = new JobsList;
window.JobView = Backbone.View.extend({
tagName: "div",
className: "item",
template: _.template($('#item-template').html()),
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this);
},
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
this.setText();
return this;
},
setText: function() {
var month=new Array();
month[0]="Jan";
month[1]="Feb";
month[2]="Mar";
month[3]="Apr";
month[4]="May";
month[5]="Jun";
month[6]="Jul";
month[7]="Aug";
month[8]="Sep";
month[9]="Oct";
month[10]="Nov";
month[11]="Dec";
var title = this.model.get('title');
var description = this.model.get('description');
var datemonth = this.model.get('datem');
var dateday = this.model.get('dated');
var jobtype = this.model.get('type');
var jobstatus = this.model.get('status');
var amount = this.model.get('amount');
var paymentstatus = this.model.get('paymentstatus')
var type = this.$('.status .jobtype');
var status = this.$('.status .jobstatus');
this.$('.title a').text(title);
this.$('.description').text(description);
this.$('.date .month').text(month[datemonth]);
this.$('.date .day').text(dateday);
type.text(jobtype);
status.text(jobstatus);
if(amount > 0)
this.$('.paymentamount').text(amount)
if(paymentstatus)
this.$('.paymentstatus').text(paymentstatus)
if(jobstatus === 'New') {
status.addClass('new');
} else if (jobstatus === 'Past Due') {
status.addClass('pastdue')
};
if(jobtype === 'Lead') {
type.addClass('lead');
} else if (jobtype === '') {
type.addClass('');
};
},
remove: function() {
$(this.el).remove();
},
clear: function() {
this.model.destroy();
}
});
window.AppView = Backbone.View.extend({
el: $("#main"),
events: {
"click #leads .highlight" : "filterLeads"
},
initialize: function() {
Jobs.bind('add', this.addOne, this);
Jobs.bind('reset', this.addAll, this);
Jobs.bind('all', this.render, this);
Jobs.fetch();
},
addOne: function(job) {
var view = new JobView({model: job});
this.$("#activitystream").append(view.render().el);
},
addAll: function() {
Jobs.each(this.addOne);
},
filterLeads: function() {
// left here, this event fires but i need to figure out how to filter the activity list.
}
});
window.App = new AppView;
});
Have you tried resetting the collection with the result of the "leads" filter?
Something like
window.AppView = Backbone.View.extend({
el: $("#main"),
events: {
"click #leads .highlight" : "filterLeads"
},
initialize: function() {
Jobs.bind('add', this.addOne, this);
Jobs.bind('reset', this.render, this); //render on reset
Jobs.fetch(); //this triggers reset
},
addOne: function(job) {
var view = new JobView({model: job});
this.$("#activitystream").append(view.render().el);
},
//add a render function
render: function() {
this.$("#activitystream").empty(); //empty out anything thats in there
Jobs.each(this.addOne);
},
filterLeads: function() {
Jobs.reset(Jobs.leads()); //reset Jobs with only the leads
}
});
Also your AppView has no 'render' method, yet you reference it here:
Jobs.bind('all', this.render, this);

Categories