Backbone.js: Using Embeded Javascript inside the View - javascript

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

Related

Single model doesn't show with router

I can not understand how to show a single product. When I want to show product page (model view - app.ProductItemView in productPageShow) I get "Uncaught TypeError: Cannot read property 'get' of undefined at child.productPageShow (some.js:58)" >> this.prod = this.prodList.get(id);
Here is my code:
// Models
var app = app || {};
app.Product = Backbone.Model.extend({
defaults: {
coverImage: 'img/placeholder.png',
id: '1',
name: 'Unknown',
price: '100'
}
});
app.ProductList = Backbone.Collection.extend({
model: app.Product,
url: 'php/listProducts.php'
});
// Views
app.ProductListView = Backbone.View.extend({
el: '#product-list',
initialize: function() {
this.collection = new app.ProductList();
this.collection.fetch({ reset: true });
this.render();
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.collection.each(function(item) {
this.renderProduct(item);
}, this);
},
renderProduct: function(item) {
app.productView = new app.ProductView({
model: item
});
this.$el.append(app.productView.render().el);
}
});
app.ProductItemView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productPage').html()),
render: function(eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
app.ProductView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productTemplate').html()),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
// Router
app.Router = Backbone.Router.extend({
routes: {
"list": 'list',
"product/:id": "productPageShow"
},
initialize: function() {
this.$content = $("#product-list");
},
list: function() {
this.prodList = new app.ProductList();
this.productListView = new app.ProductListView({ model: this.prodList });
this.prodList.fetch();
this.$content.html(app.productListView.el);
},
productPageShow: function(id) {
this.prod = this.prodList.get(id);
this.prodItView = new app.ProductItemView({ model: this.prod });
this.$content.html(this.prodItView.el);
}
});
$(function() {
new app.Router();
Backbone.history.start();
});
There are some conceptual problems with the code, without getting into too much details, there are a lot of things happening in the Router that don't belong there, but for a (currently) non-complex application that's manageable.
I'll focus on the app.Router file because that's the culprit of your problems most probably.
routes: {
"list": 'list',
"product/:id": "productPageShow"
}
Let's start with the basics, when you define a list of routes in Backbone Router( or any other Router in other frameworks ) you give a route a key that will correspond to something in the URL that the Router will recognize and call a callback method.
If you navigate your browser to:
http://your-url#list
Backbone will call the list callback
Similarly:
http://your-url#product/1
Backbone will call productPageShow callback
Thing to know: ONLY ONE ROUTE CALLBACK CAN EVER BE CALLED! The first time a Backbone Router finds a matching route it will call that callback and skip all others.
In your code you're relying on the fact that this.prodList will exist in productPageShow method but that will only happen if you first go to list route and then to product/{id} route.
Another thing .. in your listcallback in the Router you set a model on the ProductListView instance .. but that model is neither user, nor is it a model since this.productList is a Backbone.Collection
Additionally, you need to know that fetch is an asynchronous action, and you're not using any callbacks to guarantee that you'll have the data when you need it ( other than relying on the 'reset' event ).
So this would be my attempt into making this workable:
// Models
var app = app || {};
app.Product = Backbone.Model.extend({
defaults: {
coverImage: 'img/placeholder.png',
id: '1',
name: 'Unknown',
price: '100'
}
});
app.ProductList = Backbone.Collection.extend({
model: app.Product,
url: 'php/listProducts.php'
});
// Views
app.ProductListView = Backbone.View.extend({
el: '#product-list',
initialize: function() {
this.render();
this.listenTo(this.collection, 'reset', this.render);
},
render: function() {
this.collection.each(function(item) {
this.renderProduct(item);
}, this);
},
renderProduct: function(item) {
app.productView = new app.ProductView({
model: item
});
this.$el.append(app.productView.render().el);
}
});
app.ProductItemView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productPage').html()),
render: function(eventName) {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
app.ProductView = Backbone.View.extend({
tagName: 'div',
template: _.template($('#productTemplate').html()),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
// Router
app.Router = Backbone.Router.extend({
routes: {
"": "list",
"product/:id": "productPageShow"
},
initialize: function() {
this.$content = $("#product-list");
},
list: function() {
this.prodList = new app.ProductList();
this.productListView = new app.ProductListView({ collection: this.prodList });
this.prodList.fetch({reset:true});
this.$content.html(app.productListView.el);
},
productPageShow: function(id) {
try {
this.prod = this.prodList.get(id);
this.prodItView = new app.ProductItemView({ model: this.prod });
this.$content.html(this.prodItView.el);
} catch (e) {
// Navigate back to '' route that will show the list
app.Router.navigate("", {trigger:'true'})
}
}
});
$(function() {
app.Router = new app.Router();
Backbone.history.start();
});
So with a bit of shooting in the dark without the complete picture, this is what changed:
ProductListView is no longer instantiating ProductList collection that is done in the list callback in Router
Changed the route from 'list' to '', that will guarantee that the list is shown immediately
In case there are no product data available in productPageShow navigate back to list

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: bind event happen multiple times

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/

Uncaught TypeError: Object [object Window] has no method 'each' function

Hey guys here is my js file and I am taking error message about each function at line:24 and I do not know why I couldnt find whats wrong. I am just trying to see the list of items on the console.log panel but it does not even give me list on the html page.
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id){
return _.template( $('#' + id).html() );
};
App.Models.Task = Backbone.Model.extend({});
App.Collections.Task = Backbone.Collection.extend({
model: App.Models.Task
});
App.Views.Tasks = Backbone.View.extend({
tagName: 'ul',
render: function(){
this.collection.each( this.addOne, this);
return this;
},
addOne: function(task){
//creating new child view
var taskView = new App.Views.Task({ model: task });
//append to the root element
this.$el.append(taskView.render().el);
}
});
App.Views.Task = Backbone.View.extend({
tagName: 'li',
template: template('taskTemplate'),
events: {
'click .edit': 'editTask'
},
editTask: function(){
alert('you are editing the tas.');
},
render: function(){
var template = this.template( this.model.toJSON() );
this.$el.html(template);
return this;
}
});
var tasksCollection = new App.Views.Task([
{
title: 'Go to the store',
priority: 4
},
{
title: 'Go to the mall',
priority: 3
},
{
title: 'get to work',
priority: 5
}
]);
var tasksView = new App.Views.Tasks({ collection: tasksCollection });
$('.tasks').html(tasksView.render().el);
})();
You're creating a view instance as though it was a class:
App.Views.Tasks = Backbone.View.extend({ /* ... */ });
var tasksCollection = new App.Views.Task([
{
title: 'Go to the store',
priority: 4
},
//...
and then you create another instance of that view and hand it tasksCollection as though it really was a collection:
var tasksView = new App.Views.Tasks({ collection: tasksCollection });
But views and collections are different things and only collection's have an each method (unless you add an each to your view of course).
You want to create tasksCollection as an App.Collections.Task:
var tasksCollection = new App.Collections.Task([
{
title: 'Go to the store',
priority: 4
},
//...
Hi this is happening cus your each method not able to find the collection. As well the singular Task to Tasks
At this line:
Change this
var tasksCollection = new App.Views.Task([
TO, this:
var tasksCollection = new App.Collections.Tasks([

Categories