Backbone groupedBy collection is rendered only after one event click - javascript

I have problem with my groupedBy collections because is not rendered after second event click. I think there is problem with fetch my collection...but I don't know how it fix. There is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Backbone test</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
ul.items{list-style: none;width: 100%;margin: 0px;-webkit-margin-before: 0em;-webkit-padding-start: 0px;}
</style>
</head>
<body>
<header>
</header>
<content id="content">
<div id="persons"></div>
<div id="items"></div>
<div id="orders"></div>
</content>
<footer>
</footer>
<script id="itemsTemlate" type="text/template">
<div class="jumbotron">
<div class="container">
<h1>My Items!</h1>
</div>
<button class="open-orders" style="float:left;">Orders</button>
<button class="open-persons" style="float:right;">Persons</button>
</div>
<% _.each( data, function( category, i ){ %>
<h3 class="category-name"><%= i %></h3>
<div><% _.each( category, function( item ){ %>
<li class="product"><%= item.title %><p style="float:right;" class="cccc">-</p><p style="float:right;" class="cccc">+</p></li>
<% }) %>
</div>
<% }) %>
</script>
<script id="ordersTemlate" type="text/template">
<div class="jumbotron">
<div class="container">
<h1>My Orders!</h1>
</div>
<button class="open-items" style="float:left;">Items</button>
<button class="open-persons" style="float:right;">Persons</button>
</div>
<% _.each( data, function( category, i ){ %>
<h3 class="category-name"><%= i %></h3>
<div><% _.each( category, function( order ){ %>
<li class="order"><%= order.title %><p style="float:right;" class="cccc">X</p></li>
<% }) %>
</div>
<% }) %>
</script>
<script id="personsTemlate" type="text/template">
<div class="jumbotron">
<div class="container">
<h1>My Persons!</h1>
</div>
<button class="open-items" style="float:left;">Items</button>
<button class="open-orders" style="float:right;">Orders</button>
</div>
<% _.each( data, function( category, i ){ %>
<h3 class="category-name"><%= i %></h3>
<div><% _.each( category, function( person ){ %>
<li class="product"><%= person.title %><p style="float:right;" class="cccc">X</p></li>
<% }) %>
</div>
<% }) %>
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.1/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
<script>
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
window.vent = _.extend({}, Backbone.Events);
})();
// !models.js
App.Models.Item = Backbone.Model.extend({});
App.Models.Person = Backbone.Model.extend({});
App.Models.Order = Backbone.Model.extend({});
// !collections.js
App.Collections.Items = Backbone.Collection.extend({
model: App.Models.Item,
url: 'api/items.json'
});
App.Collections.Persons = Backbone.Collection.extend({
model: App.Models.Person,
url: 'api/persons.json'
});
App.Collections.Orders = Backbone.Collection.extend({
model: App.Models.Order,
url: 'api/orders.json'
});
// !views.js
App.Views.Items = Backbone.View.extend({
el: '#items',
events: {
'click button.open-orders':'openOrders',
'click button.open-persons':'openPersons',
},
openOrders: function() {
this.remove();
this.unbind();
App.myRouter.navigate("orders", {trigger: true, replace: true});
console.log("openOrders");
},
openPersons: function() {
this.remove();
this.unbind();
App.myRouter.navigate("persons", {trigger: true, replace: true});
console.log("openPersons");
},
initialize: function() {
this.listenTo( this.collection, "change", this.render );
this.template = _.template( document.getElementById('itemsTemlate').innerHTML );
this.render();
this.$el;
},
getGroups : function(){
return _.groupBy(this.collection.toJSON(), 'category');
},
render: function() {
this.el.innerHTML = this.template({ data : this.getGroups() });
},
});
App.Views.Persons = Backbone.View.extend({
el: '#persons',
events: {
'click button.open-items':'openItems',
'click button.open-orders':'openOrders',
},
openItems: function() {
this.remove();
this.unbind();
App.myRouter.navigate("items", {trigger: true, replace: true});
},
openOrders: function() {
this.remove();
this.unbind();
App.myRouter.navigate("orders", {trigger: true, replace: true});
},
initialize: function() {
this.listenTo( this.collection, "change", this.render );
this.template = _.template( document.getElementById('personsTemlate').innerHTML );
this.render();
this.$el;
},
getGroups : function(){
return _.groupBy(this.collection.toJSON(), 'category');
},
render: function() {
this.el.innerHTML = this.template({ data : this.getGroups() });
},
});
App.Views.Orders = Backbone.View.extend({
el: '#orders',
events: {
'click button.open-items':'openItems',
'click button.open-persons':'openPersons',
},
openItems: function() {
this.remove();
this.unbind();
App.myRouter.navigate("items", {trigger: true, replace: true});
},
openPersons: function() {
this.remove();
this.unbind();
App.myRouter.navigate("persons", {trigger: true, replace: true});
},
initialize: function() {
this.listenTo( this.collection, "change", this.render );
this.template = _.template( document.getElementById('ordersTemlate').innerHTML );
this.render();
this.$el;
},
getGroups : function(){
return _.groupBy(this.collection.toJSON(), 'category');
},
render: function() {
this.el.innerHTML = this.template({ data : this.getGroups() });
},
});
// !router.js
App.Router = Backbone.Router.extend({
routes: {
'':'persons',
'persons':'persons',
'items':'items',
'orders':'orders'
},
persons: function() {
App.persons = new App.Collections.Persons;
App.persons.fetch().then(function() {
new App.Views.Persons({ collection: App.persons });
});
console.log('persons page !');
},
items: function() {
App.items = new App.Collections.Items;
App.items.fetch().then(function() {
new App.Views.Items({ collection: App.items });
});
console.log('items page !');
},
orders: function() {
App.orders = new App.Collections.Orders;
App.orders.fetch().then(function() {
new App.Views.Orders({ collection: App.orders });
});
console.log('orders page !');
},
});
App.myRouter = new App.Router();
Backbone.history.start();
</script>
</body>
</html>

Sounds like you could use an itemView. This is when marionette comes in very handy.
check out marionette

Related

how to use hogan template with backbone?

How to use hogan templates with backbone code?How to combine the values of a model into the Hogan templates?
My HTML page:
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Backbone.js • TodoMVC</title>
<link rel="stylesheet" href="js/assets/base.css">
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus>
</header>
<section id="main">
<input id="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"></ul>
</section>
<footer id="footer"></footer>
</section>
<div id="info">
<p>Double-click to edit a todo</p>
<p>Written by Addy Osmani</p>
<p>Part of TodoMVC</p>
</div>
<script type="text/template" id="item-template">
<div class="view">
<input class="toggle" type="checkbox" {{ completed ? 'checked' : '' }}>
<label>{{ title }}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="{{ title }}">
</script>
<script type="text/template" id="stats-template">
<span id="todo-count"><strong>{{ remaining }}</strong> {{ remaining === 1 ? 'item' : 'items' }} left</span>
<ul id="filters">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
Active
</li>
<li>
Completed
</li>
</ul>
{[ if (completed) { ]}
<button id="clear-completed">Clear completed ({{ completed }})</button>
{[ } ]}
</script>
<script src="js/assets/base.js"></script>
<script src="js/lib/jquery.js"></script>
<script src="js/lib/underscore.js"></script>
<script src="js/lib/backbone.js"></script>
<script src="js/lib/backbone.localStorage.js"></script>
<script src="js/models/todo.js"></script>
<script src="js/collections/todos.js"></script>
<script src="js/views/todos.js"></script>
<script src="js/views/app.js"></script>
<script src="js/routers/router.js"></script>
<script src="js/app.js"></script>
</body>
</html>
My js code
/*global Backbone, jQuery, _, ENTER_KEY */
var app = app || {};
(function ($) {
'use strict';
_.templateSettings = {
evaluate : /\{\[([\s\S]+?)\]\}/g,
interpolate : /\{\{([\s\S]+?)\}\}/g
};
// The Application
// ---------------
// Our overall **AppView** is the top-level piece of UI.
app.AppView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: '#todoapp',
// Our template for the line of statistics at the bottom of the app.
statsTemplate: _.template($('#stats-template').html()),
// Delegated events for creating new items, and clearing completed ones.
events: {
'keypress #new-todo': 'createOnEnter',
'click #clear-completed': 'clearCompleted',
'click #toggle-all': 'toggleAllComplete'
},
// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *localStorage*.
initialize: function () {
this.allCheckbox = this.$('#toggle-all')[0];
this.$input = this.$('#new-todo');
this.$footer = this.$('#footer');
this.$main = this.$('#main');
this.$list = $('#todo-list');
this.listenTo(app.todos, 'add', this.addOne);
this.listenTo(app.todos, 'reset', this.addAll);
this.listenTo(app.todos, 'change:completed', this.filterOne);
this.listenTo(app.todos, 'filter', this.filterAll);
this.listenTo(app.todos, 'all', this.render);
// Suppresses 'add' events with {reset: true} and prevents the app view
// from being re-rendered for every model. Only renders when the 'reset'
// event is triggered at the end of the fetch.
app.todos.fetch({reset: true});
},
// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function () {
var completed = app.todos.completed().length;
var remaining = app.todos.remaining().length;
if (app.todos.length) {
this.$main.show();
this.$footer.show();
this.$footer.html(this.statsTemplate({
completed: completed,
remaining: remaining
}));
this.$('#filters li a')
.removeClass('selected')
.filter('[href="#/' + (app.TodoFilter || '') + '"]')
.addClass('selected');
} else {
this.$main.hide();
this.$footer.hide();
}
this.allCheckbox.checked = !remaining;
},
// Add a single todo item to the list by creating a view for it, and
// appending its element to the `<ul>`.
addOne: function (todo) {
var view = new app.TodoView({ model: todo });
this.$list.append(view.render().el);
},
// Add all items in the **Todos** collection at once.
addAll: function () {
this.$list.html('');
app.todos.each(this.addOne, this);
},
filterOne: function (todo) {
todo.trigger('visible');
},
filterAll: function () {
app.todos.each(this.filterOne, this);
},
// Generate the attributes for a new Todo item.
newAttributes: function () {
return {
title: this.$input.val().trim(),
order: app.todos.nextOrder(),
completed: false
};
},
// If you hit return in the main input field, create new **Todo** model,
// persisting it to *localStorage*.
createOnEnter: function (e) {
if (e.which !== ENTER_KEY || !this.$input.val().trim()) {
return;
}
app.todos.create(this.newAttributes());
this.$input.val('');
},
// Clear all completed todo items, destroying their models.
clearCompleted: function () {
_.invoke(app.todos.completed(), 'destroy');
return false;
},
toggleAllComplete: function () {
var completed = this.allCheckbox.checked;
app.todos.each(function (todo) {
todo.save({
'completed': completed
});
});
}
});
})(jQuery);
it is from the ToDo example. I used Mustache templates here by changing the template settings. But i want to convert the template into Hogan.
my Edited js:
/*global Backbone, jQuery, _, ENTER_KEY, ESC_KEY */
var app = app || {};
(function ($) {
'use strict';
// Todo Item View
// --------------
_.templateSettings = {
evaluate : /\{\[([\s\S]+?)\]\}/g,
interpolate : /\{\{([\s\S]+?)\}\}/g
};
// The DOM element for a todo item...
app.TodoView = Backbone.View.extend({
tagName: 'li',
// Cache the template function for a single item.
template1: _.template($('#item-template').html()),
template:Hogan.compile(template1),
// The DOM events specific to an item.
events: {
'click .toggle': 'toggleCompleted',
'dblclick label': 'edit',
'click .destroy': 'clear',
'keypress .edit': 'updateOnEnter',
'keydown .edit': 'revertOnEscape',
'blur .edit': 'close'
},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
this.listenTo(this.model, 'visible', this.toggleVisible);
},
render: function () {
// Backbone LocalStorage is adding `id` attribute instantly after creating a model.
// This causes our TodoView to render twice. Once after creating a model and once on `id` change.
// We want to filter out the second redundant render, which is caused by this `id` change.
// It's known Backbone LocalStorage bug, therefore we've to create a workaround.
// https://github.com/tastejs/todomvc/issues/469
if (this.model.changed.id !== undefined) {
return;
}
console.log(this.template);
this.$el.html(this.template(this.model.toJSON()));
this.$el.toggleClass('completed', this.model.get('completed'));
this.toggleVisible();
this.$input = this.$('.edit');
return this;
},
toggleVisible: function () {
this.$el.toggleClass('hidden', this.isHidden());
},
isHidden: function () {
var isCompleted = this.model.get('completed');
return (// hidden cases only
(!isCompleted && app.TodoFilter === 'completed') ||
(isCompleted && app.TodoFilter === 'active')
);
},
toggleCompleted: function () {
this.model.toggle();
},
edit: function () {
this.$el.addClass('editing');
this.$input.focus();
},
close: function () {
var value = this.$input.val();
var trimmedValue = value.trim();
if (!this.$el.hasClass('editing')) {
return;
}
if (trimmedValue) {
this.model.save({ title: trimmedValue });
if (value !== trimmedValue) {
this.model.trigger('change');
}
} else {
this.clear();
}
this.$el.removeClass('editing');
},
updateOnEnter: function (e) {
if (e.which === ENTER_KEY) {
this.close();
}
},
revertOnEscape: function (e) {
if (e.which === ESC_KEY) {
this.$el.removeClass('editing');
}
},
clear: function () {
this.model.destroy();
}
});
})(jQuery);

Backbone error Uncaught TypeError: Cannot call method 'each' of undefined?

I have next problem with function each. I wanna render all tables from collection, after that add function for create new table. But I don't know why I have in console error:
Uncaught TypeError: Cannot call method 'each' of undefined
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Backbone test</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0-rc1/css/bootstrap.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<header>
<div class="jumbotron">
<div class="container">
<h1>My tables!</h1>
</div>
</div>
</header>
<content>
<div id="add-table">
<div class="container">
<label>Add new table!</label>
<form>
<legend>Name</legend>
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default" type="button">Go!</button>
</span>
<input type="text" class="form-control">
</div>
</form>
</div>
</div>
<div id="content"></div>
</content>
<script id="allTableTemlate" type="text/template">
<li><%= name %></li>
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.1/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
<script>
// !main.js
(function() {
window.App = {
Models: {},
Collections: {},
Views: {},
Router: {}
};
window.vent = _.extend({}, Backbone.Events);
window.template = function(id) {
return _.template( $('#' + id).html() );
};
})();
// !models.js
App.Models.Table = Backbone.Model.extend({});
// !collections.js
App.Collections.Tables = Backbone.Collection.extend({
model: App.Models.Table,
url: 'tables.json'
});
// !views.js
App.Views.App = Backbone.View.extend({
initialize: function() {
var allTablesView = new App.Views.Tables({ collection: App.tables }).render();
$(document.body).append(allTablesView.el);
}
});
App.Views.Tables = Backbone.View.extend({
tagName: 'ul',
render: function() {
this.collection.each( this.addOne, this );
return this;
},
addOne: function(table) {
var tableView = new App.Views.Tables({ model: table });
this.$el.append(tableView.render().el);
},
});
App.Views.Table = Backbone.View.extend({
tagName: 'li',
template: template('allTableTemlate'),
render: function() {
this.$el.html( this.template( this.model.toJSON() ) );
return this;
},
});
// !router.js
App.Router = Backbone.Router.extend({
routes: {
'':'index',
},
index: function() {
console.log('index page !');
},
});
// !index.html
new App.Router;
Backbone.history.start();
App.tables = new App.Collections.Tables;
App.tables.fetch().then(function() {
new App.Views.App({ collection: App.tables });
});
</script>
</body>
</html>
This is my complete code with json data :
[
{"name": "Table 1","stts": "redstts","id": 1},
{"name": "Table 2","stts": "redstts","id": 2},
{"name": "Table 3","stts": "redstts","id": 3},
{"name": "Table 4","stts": "redstts","id": 4},
{"name": "Table 5","stts": "redstts","id": 5}
]
There is a typo in your addOne function. You should be creating a new "App.Views.Table" instead of "new App.Views.Tables" :
addOne: function(table) {
var tableView = new App.Views.Table({ model: table });
this.$el.append(tableView.render().el);
}

Why all but 1 Route is working with Backbone Router

In the script below are various routes and view and routes events built with Backbone.js
Why is it, that all of the routes work as I expect except the last one 'products'.
I originally had it as a very different function but realized it wasn't firing off, but not even as a duplicate of the other views and routes it still wont work.
Anyone have an idea why?
Thanks you!
I am also very new to Backbone.
Rich
<!doctype html>
<html>
<head>
<title>Undie Couture by Lauren Copeland</title>
<link type="text/css" rel="stylesheet" href="/assets/css/museosans_500_macroman/stylesheet.css" />
<link type="text/css" rel="stylesheet" href="/assets/css/bootstrap/bootstrap.css" />
<link type="text/css" rel="stylesheet" href="/assets/css/site/front-styles.css" />
</head>
<body>
<div id="wrapper">
<div class="content">
<header>
<div class="container">
<div id="logo"></div>
<nav>
<ul>
<li>shop</li>
<li>contact</li>
<li>about</li>
<li>wholesale</li>
</nav>
</nav>
</div>
</header>
<div class="container">
<div class="page" id="first" style="display:none;"></div>
<div class="page"></div>
</div>
</div>
</div>
</body>
<script type="text/template" id="title-temp">
<%= title %>
</script>
<script type="text/template" id="logo-temp">
<img src="/assets/img/logo-strip.png" />
</script>
<script type="text/template" id="home-temp">
<%= title %>
</script>
<script type="text/template" id="page-temp">
<h1><%= page.pluck('title') %></h1>
<div id="body">
<%= page.pluck('body') %>
</div>
</script>
<script type="text/template" id="product-temp">
<h1><%= page.pluck('name') %></h1>
<div id="body">
<%= page.pluck('description') %>
</div>
</script>
<script type="text/javascript" src="/assets/js/libs/jquery/jquery.js"> </script>
<script type="text/javascript" src="/assets/js/libs/underscore/underscore.js"></script>
<script type="text/javascript" src="/assets/js/libs/backbone/backbone-min.js"></script>
<script type="text/javascript" src="/assets/js/libs/bootstrap/bootstrap.js"></script>
<script type = "text/javascript">
var Router = Backbone.Router.extend({
routes: {
"home": "home",
"about": "about",
"contact": "contact",
"wholesale": "wholesale",
"products": "products"
}
});
var Page = Backbone.Model.extend({
initialize: function() {
console.log('Page model loaded');
},
defaults: {
"id": "",
"title": "",
"body": "",
"slug": ""
},
urlRoot: '/backbone/page'
});
var Pages = Backbone.Collection.extend({
initialize: function() {
console.log('pages collection loaded');
},
url: '/backbone/page'
});
var HomeView = Backbone.View.extend({
template: $('#standard').html(),
el: '.page:first',
change: function() {
$('.page').fadeOut('slow');
},
render: function() {
logo = $('#logo-temp').html();
$('#logo').html(logo);
$('.content').attr('id', 'home');
var that = this;
that.change();
compiled = _.template($('#home-temp').html(), {
title: ''
});
that.$el.html(compiled);
}
});
var AboutView = Backbone.View.extend({
template: $('#standard').html(),
el: '.page:first',
change: function() {},
render: function() {
console.log('render')
var that = this;
logo = $('#logo-temp').html();
$('#logo').html(logo);
$('.content').attr('id', 'about');
aboutPage = new Pages();
aboutPage.fetch({
data: {
id: 3
},
success: function() {
$('#first').fadeOut({
duration: 400,
complete: function() {
console.log(aboutPage.models)
compiled = _.template($('#page-temp').html(), {
page: aboutPage
});
that.$el.html(compiled).delay(300).fadeIn();
}
});
}
});
}
});
var ContactView = Backbone.View.extend({
template: $('#standard').html(),
el: '.page:first',
change: function() {
$('.page').fadeOut();
},
render: function() {
var that = this;
logo = $('#logo-temp').html();
$('#logo').html(logo);
$('.content').attr('id', 'contact');
contactPage = new Pages();
contactPage.fetch({
data: {
id: 2
},
success: function() {
$('#first').fadeOut({
duration: 400,
complete: function() {
console.log(contactPage.models)
compiled = _.template($('#page-temp').html(), {
page: contactPage
});
that.$el.html(compiled).delay(300).fadeIn();
}
});
}
});
}
});
var WholesaleView = Backbone.View.extend({
template: $('#standard').html(),
el: '.page:first',
change: function() {},
render: function() {
console.log('render')
var that = this;
logo = $('#logo-temp').html();
$('#logo').html(logo);
$('.content').attr('id', 'about');
wholePage = new Pages();
wholePage.fetch({
data: {
id: 4
},
success: function() {
$('#first').fadeOut({
duration: 400,
complete: function() {
console.log(wholePage.models)
compiled = _.template($('#page-temp').html(), {
page: wholePage
});
that.$el.html(compiled).delay(300).fadeIn();
}
});
}
});
}
});
var ProductView = Backbone.View.extend({
template: $('#standard').html(),
el: '.page:first',
change: function() {},
render: function() {
var that = this;
logo = $('#logo-temp').html();
$('#logo').html(logo);
$('.content').attr('id', 'about');
wholePage = new Pages();
wholePage.fetch({
data: {
id: 4
},
success: function() {
$('#first').fadeOut({
duration: 400,
complete: function() {
console.log(wholePage.models)
compiled = _.template($('#page-temp').html(), {
page: wholePage
});
that.$el.html(compiled).delay(300).fadeIn();
}
});
}
});
}
});
var router = new Router();
var page_model = new Page();
var page_col = new Pages();
var home = new HomeView();
var about = new AboutView();
var contact = new ContactView();
var wholesale = new WholesaleView();
var products = new ProductView();
router.on('route:home', function() {
home.render();
});
router.on('route:about', function() {
about.render();
});
router.on('route:contact', function() {
contact.render();
});
router.on('route:wholesale', function() {
wholesale.render();
});
router.on('route:products', function() {
products.render();
});
Backbone.history.start();
</script>
Your anchor tag for shops has a trailing /
<a href="/#/products/">
Should be
<a href="/#/products">
Removing that made the route work for me.

Backbone change event is not firing

I am learning Backbone and struck at one place. I wanted to load model on page load. So I have below code.
<html>
<head>
<script src="js/jquery.min.js"></script>
<script src="js/underscore-min.js"></script>
<script src="js/backbone-min.js"></script>
<script src="js/json2.js"></script>
</head>
<body>
<h1></h1>
<div id="poview"></div>
<script id="poTemplate" type="text/template">
<td><%= Ponumber %></td>
<td><%= Posuppliername %></td>
<td><%= Postatusname %></td>
<td><%= Podate %></td>
<td><%= DispOrderTotal %></td>
</script>
<script type="text/javascript">
(function($) {
var PO = Backbone.Model.extend()
var POList = Backbone.Collection.extend({
url: 'po.json',
model: PO,
parse: function(response) {
console.log(response.PurchaseOrder)
return response.PurchaseOrder
}
})
var POView = Backbone.View.extend({
tagName: 'tr',
template: $('#poTemplate').html(),
initialize: function() {
_.bindAll(this, 'render')
this.model.bind('change', this.render)
},
events: {
'click': 'click'
},
render: function() {
console.log('po render')
var tmpl = _.template(this.template)
$(this.el).html(tmpl(this.model.toJSON()))
return this;
},
click: function() {
console.log('clicked....')
}
})
var POListView = Backbone.View.extend({
el: $('#poview'),
initialize: function() {
_.bindAll(this, 'render', 'appendPO')
this.collection = new POList()
this.collection.bind('change', this.render, this)
this.collection.bind('add', this.render, this)
this.collection.fetch({add:true})
},
render: function() {
var self = this;
console.log(this.collection.models)
this.collection.each(function(po) {
self.appendPO(po)
}, this)
},
appendPO: function(po) {
var poView = new POView({
model: po
});
console.log(po)
$(this.el).append(poView.render().el)
}
});
var poListView = new POListView()
})(jQuery)
</script>
</body>
</html>
And below is json response from server
{
"#totalCount": "1134",
"PurchaseOrder": [
{
"ID": "689600",
"Ponumber": "K037412201",
"Poname": "",
"Podate": "2011-12-26T10:03:24.000+05:30",
"Posuppliername": "XYZ UPS Supplier",
"Postatusname": "Approved - Supplier Received",
"DispOrderTotal": "$1.99"
},
{
"ID": "689601",
"Ponumber": "K037412202",
"Poname": "",
"Podate": "2011-12-26T10:03:24.000+05:30",
"Posuppliername": "ABC UPS Supplier",
"Postatusname": "Approved - Supplier Received",
"DispOrderTotal": "$1.99"
}
]
}
But when the page is loaded render method on POListView is not fired. What is issue in this code?
Edit
jQuery(function($){
...
});
If I use above convention also, that does not work.
Working example
Refer answer from #JayC
<html>
<head>
<script src="js/jquery.min.js"></script>
<script src="js/underscore.js"></script>
<script src="js/backbone.js"></script>
<script src="js/json2.js"></script>
</head>
<body>
<h1></h1>
<div id="poview"></div>
<script id="poTemplate" type="text/template">
<td><%= Ponumber %></td>
<td><%= Posuppliername %></td>
<td><%= Postatusname %></td>
<td><%= Podate %></td>
<td><%= DispOrderTotal %></td>
</script>
<script type="text/javascript">
jQuery(function($) {
var PO = Backbone.Model.extend({
idAttribute: 'ID'
})
var POList = Backbone.Collection.extend({
url: 'po.json',
model: PO,
parse: function(response) {
console.log('parse')
return response.PurchaseOrder
}
})
var POView = Backbone.View.extend({
tagName: 'tr',
template: $('#poTemplate').html(),
initialize: function() {
_.bindAll(this, 'render', 'click')
},
events: {
'click': 'click'
},
render: function() {
var tmpl = _.template(this.template)
$(this.el).html(tmpl(this.model.toJSON()))
return this;
},
click: function() {
console.log('clicked.... ' + this.model.id)
}
})
var POListView = Backbone.View.extend({
el: $('#poview'),
initialize: function() {
_.bindAll(this, 'render', 'appendPO')
this.collection = new POList()
this.collection.bind('reset', this.render, this)
this.collection.fetch()
},
render: function() {
var self = this;
console.log(this.collection.models)
this.collection.each(function(po) {
self.appendPO(po)
}, this)
},
appendPO: function(po) {
var poView = new POView({
model: po
});
$(this.el).append(poView.render().el)
}
});
var poListView = new POListView()
});
</script>
</body>
</html>
I'm not seeing a binding to the reset event for your collection. The Backbone.js documentation now has a FAQ which explains events (finally!) and you can see from it that
the reset event fires when the collection's entire contents have been replaced. On collections, that's what fetch normally does.
You wrote this, which executes the function immediately, when the DOM is not yet ready/available:
(function($){
...
})(jQuery);
But probably you wanted to write this (see http://api.jquery.com/jQuery/#jQuery3 ), which delays the function until the document is ready to be manipulated (attach your views, etc):
jQuery(function($){
...
});
Of course, only the actual initialization call should be wrapped into jQuery(), the other parts can remain in the plain closure.

Backbone.js - Get JSON array into view template

window.User = Backbone.Model.extend({
defaults: {
name: 'Jane',
friends: []
},
urlRoot: "users",
initialize: function(){
this.fetch();
}
});
var HomeView = Backbone.View.extend({
el: '#container',
template: _.template($("#home-template").html()),
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
home: function() {
var user = new User({id: 1});
this.homeView = new HomeView({
model: user
});
this.homeView.render();
},
The model data is being queried and the root level attributes work fine, but the attribute that contains an array of other objects doesn't seem to show up.
Template:
<script id="home-template" type="text/template">
<div id="id">
<div class="name"><%= name %></div>
<br />
<h3> Single Friends</h3>
<br />
<ul data-role="listview" data-inset="true", data-filter="true">
<% _.each(friends, function(friend) { %>
<li>
<a href="/profile?id=<%= friend.id %>", data-ajax="false">
<div class="picture"><img src="http://graph.facebook.com/<%= friend.fb_user_id %>/picture"></div>
<div class="name"><%= friend.name %></div>
</a>
</li>
<% }); %>
</ul>
</div>
</script>
Return JSON:
{"name":"John Smith","friends":[{"id":"1234","name":"Joe Thompson","fb_user_id":"4564"},{"id":"1235","name":"Jane Doe","fb_user_id":"4564"}]}
It almost seems like it's not seeing the .friends attribute at all because it's taking the defaults of the model ([]).
Any suggestions?
You are calling render() before fetch() has returned the data from the server.
Try this?
window.User = Backbone.Model.extend({
defaults: {
name: 'Jane',
friends: []
},
urlRoot: "users"
});
var HomeView = Backbone.View.extend({
el: '#container',
template: _.template($("#home-template").html()),
initialize: function() {
this.model.fetch();
this.model.bind('change', this.render, this);
}
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});

Categories