i am working on learn backbone.js, so i decided to make mi own Todo app using backbone.js and a localstorage plugin. I already have the Todo app in wich you can add and remove todos, now I am working on make them sortable in done and not done tasks. But the problem is that i can't find a way to do it. i created a method in the view called sort_done()
sort_done: function(){
var done = this.collection.where({done: true});
console.log(done);
}
But don't know how could i udate the view in order to just show thedone or not done tasks, i appreciate if someone could give me some advice in how i can manage this kind of ´problem. I also leave the model, collection and views js so you can take a look.
Model:
var Task = Backbone.Model.extend({
defaults:{
title: "An empyt task..",
done: false
},
validate: function(attrs){
if(! $.trim(attrs.title)){
return "The task has no title"
}
}
});
var task = new Task;
Collection:
var Tasks = Backbone.Collection.extend({
model: Task,
localStorage: new Backbone.LocalStorage("todos-collection")
});
var tasks = new Tasks();
Views:
var TaskView = Backbone.View.extend({
tagName: "li",
template: _.template( $('#task').html() ),
initialize: function(){
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
render: function(){
var template = this.template( this.model.toJSON() );
this.$el.html( template );
return this;
},
events: {
'click .icon-checkbox': 'toggleState',
'click .task_title': 'editTask',
'keypress .edit': 'updateOnEnter',
'click .close_btn': 'clear'
},
toggleState: function(e){
var $checkbox = $(e.target);
this.model.save('done', !this.model.get('done'));
},
editTask: function(e){
this.task = $(e.target);
this.editBox = this.task.next();
this.editInput = this.editBox.find('.edit');
$(".task_title").removeClass("display__none");
$(".editBox").removeClass("edit_box__editing");
this.task.addClass("display__none")
this.editBox.addClass("edit_box__editing");
this.editInput.attr('placeholder', this.task.text()).focus();
},
updateOnEnter: function(e){
if(e.keyCode === 13){
this.close();
}
},
close: function(){
var value = this.editInput.val();
if(!value){
this.task.removeClass("display__none")
this.editBox.removeClass("edit_box__editing");
}else{
this.model.save({title: value});
this.task.removeClass("display__none")
this.editBox.removeClass("edit_box__editing");
}
},
clear:function(){
this.model.destroy();
}
});
var TasksView = Backbone.View.extend({
el: '#tasks',
initialize: function(){
this.render();
this.collection.on('add', this.addOne, this);
this.collection.on()
},
render: function(){
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task){
var taskView = new TaskView({ model: task });
this.$el.append( taskView.render().el );
}
});
var AddTask = Backbone.View.extend({
el: '#todos',
initialize: function(){
this.collection.fetch();
},
events:{
'click #add': 'addTask',
'click #filter_done': 'sort_done',
'keypress #inputTask': 'updateOnEnter'
},
addTask: function(){
var taskTitle = $('#inputTask'). val();
$('#inputTask').val(""); //clear the input
if($.trim(taskTitle) === ''){//check if the input has some text in it
this.displayMessage("Todo's can not be empty");
}else{
var task = new Task( {title: taskTitle} ); // create the task model
this.collection.create(task); //add the model to the collection
}
},
displayMessage: function(msg){
$('#inputTask').focus().attr("placeholder", msg);
},
updateOnEnter: function(e){
if(e.keyCode === 13){
this.addTask();
}
},
sort_done: function(){
var done = this.collection.where({done: true});
console.log(done);
}
});
var tasksView = new TasksView( {collection: tasks} );
var addTask = new AddTask( {collection: tasks} );
Thank yoou very much!
When you call collection.where your collection is not filtered, it just returning the filtered models (not changing the initial collection), so for your problem you have to do like this :
initialize: function(){
this.render();
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.render, this); // here I change the event to reset
},
...
sort_done: function(){
var done = this.collection.where({done: true});
this.collection.reset(done);
console.log(done);
}
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);
}
});
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.
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
});
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);
I have created the following code and i'm unable to destroy a model from the backend.
I get a 'model is not defined error'.
I don't know why it cannot find a model? what parameters am i missing? am i wrong in adding this in this list view? should i add it in the models view? and if that is the case i added the save new model in the list view so can't see why i can't add it her.
window.LibraryView = Backbone.View.extend({
tagName: 'section',
className: 'mynotes',
events: {
"keypress #new-title": "createOnEnter",
//"keypress #new-content": "createOnEnter"
"click .mybutton": "clearCompleted"
},
initialize: function() {
_.bindAll(this, 'render');
this.template = _.template($('#library-template').html());
this.collection.bind('reset', this.render);
this.collection.bind('add', this.render);
this.collection.bind('remove', this.render);
},
render: function() {
var $mynotes,
collection = this.collection;
$(this.el).html(this.template({}));
$mynotes = this.$(".mynotes");
collection.each(function(mynote) {
var view = new LibraryMynoteview({
model: mynote,
collection: collection
});
$mynotes.append(view.render().el);
});
return this;
},
createOnEnter: function(e) {
var input = this.$("#new-title");
var input2 = this.$("#new-content");
//var msg = this.model.isNew() ? 'Successfully created!' : "Saved!";
if (!input || e.keyCode != 13) return;
// this.model.save({title: this.$("#new-title").val(), content: this.$("#new-content").val() }, {
var newNote = new Mynote({title: this.$("#new-title").val(), content: this.$("#new-content").val()});
this.collection.create(newNote);
},
clearCompleted: function() {
this.model.destroy();
this.collection.remove();
}
});
You have to bind your clearCompleted method to this:
_.bindAll(this, 'render', 'clearCompleted');