duplicate POST, GET and DELETE request using backbone js - javascript

I have a simle CRUD application. But when i do delete , create or edit Nth time N number of request get fired.
For example If i am creating a user 3rd time the 3 POST request gets fired.
when i refresh the page only one request is fired. Please help. below is my code.
HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My App</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1>User Manager</h1>
<hr />
<div class="page"> </div>
</div>
<script src="http://code.jquery.com/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="http://underscorejs.org/underscore-min.js" type="text/javascript"></script>
<script src="http://backbonejs.org/backbone-min.js" type="text/javascript"></script>
<script type="text/javascript">
function htmlEncode(value){
return $('<div/>').text(value).html();
}
</script>
<script type="text/template" id="user-list-template">
New
<hr />
<table class="table striped">
<thead>
<tr>
<th>First Name</th><th>Last Name</th><th>Age</th><th></th>
</tr>
</thead>
<tbody>
<% _.each(users, function(user) { %>
<tr>
<td><%= htmlEncode(user.get('firstname')) %></td>
<td><%= htmlEncode(user.get('lastname')) %></td>
<td><%= htmlEncode(user.get('age')) %></td>
<td><a class="btn" href="#/edit/<%= user.id %>">Edit</a></td>
</tr>
<% }); %>
</tbody>
</table>
</script>
<script type="text/template" id="edit-user-template">
<form class="edit-user-form">
<legend><%= user ? 'Edit' : 'New' %> User</legend>
<label>First Name</label>
<input name="firstname" type="text" value="<%= user ? user.get('firstname') : '' %>">
<label>Last Name</label>
<input name="lastname" type="text" value="<%= user ? user.get('lastname') : '' %>">
<label>Age</label>
<input name="age" type="text" value="<%= user ? user.get('age') : '' %>">
<hr />
<button type="submit" class="btn"><%= user ? 'Update' : 'Create' %></button>
<% if(user) { %>
<input type="hidden" name="id" value="<%= user.id %>" />
<button data-user-id="<%= user.id %>" class="btn btn-danger delete">Delete</button>
<% }; %>
</form>
</script>
<script src="user.js" type="text/javascript"></script>
</body>
</html>
JAVASCRIPT
var Myapp ={};
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.url = 'http://backbonejs-beginner.herokuapp.com' + options.url;
});
// Setting up user collection
Myapp.Users = Backbone.Collection.extend({
url: '/users'
});
// setting up user model
Myapp.User = Backbone.Model.extend({
urlRoot: '/users',
validate: function(attrs, options) {
if (attrs.firstname =='') {
alert("First Name cannot be blank.");
return "First Name cannot be blank.";
}
}
});
// setting up routes
Myapp.Router = Backbone.Router.extend({
routes: {
"": "home",
"edit/:id": "edit",
"new": "edit",
}
});
Myapp.router = new Myapp.Router;
Myapp.router.on('route:home', function() {
// render user list
Myapp.displayUsersModule.init().render();
})
Myapp.router.on('route:edit', function(id) {
Myapp.editUserViewModule.init().render({id: id});
})
// display user module imkplemented in module patern. The only public api is the init function
Myapp.displayUsersModule = (function(){
var UserListView = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var users = new Myapp.Users();
users.fetch({
success: function (users) {
var template = _.template($('#user-list-template').html(), {users: users.models});
that.$el.html(template);
},
error: function (model, xhr, options) {
alert("Something went wrong while fetching details");
}
})
}
});
var init = function() {
userListView = new UserListView();
return userListView;
};
return {
init: init
};
})();
// edit and delete user module imkplemented in module patern. The only public api is the init function
Myapp.editUserViewModule = (function(){
var UserEditView = Backbone.View.extend({
el: '.page',
events: {
'submit .edit-user-form': 'saveUser',
'click .delete': 'deleteUser'
},
saveUser: function (ev) {
var userDetails = $(ev.currentTarget).serializeObject();
var user = new Myapp.User();
user.save(userDetails, {
success: function (user) {
Myapp.router.navigate('', {trigger:true});
},
error: function (model, xhr, options) {
alert("Something went wrong while saving details");
}
});
return false;
},
deleteUser: function (ev) {
alert(1);
this.user.destroy({
success: function () {
console.log('destroyed');
Myapp.router.navigate('', {trigger:true});
},
error: function (model, xhr, options) {
alert("Something went wrong while Deleting user");
}
});
return false;
},
render: function (options) {
var that = this;
if(options.id) {
that.user = new Myapp.User({id: options.id});
that.user.fetch({
success: function (user) {
var template = _.template($('#edit-user-template').html(), {user: user});
that.$el.html(template);
}
})
} else {
var template = _.template($('#edit-user-template').html(), {user: null});
that.$el.html(template);
}
}
});
var init = function() {
var userEditView = new UserEditView();
return userEditView;
};
return {
init: init
};
})();
$(document).ready(function(){
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
Backbone.history.start();
});

The problem is that you don't correct unbind views when switching them, so you have multiple zombie view that listen to the same DOM events (it is because you attach them to DOM using el: 'selector' style that is not pretty good).
To fix it you should call remove (or undelegateEvents) method when you don't need it anymore.
Or you can use libraries that help you to manage views such as:
backbone.layoutmanager
backbone.marionette

Related

Knockout JS How to Access a Value from Second View Model / Binding

I am new to Knockout JS and think it is great. The documentation is great but I cannot seem to use it to solve my current problem.
The Summary of my Code
I have two viewmodels represented by two js scripts. They are unified in a parent js file. The save button's event is outside
both foreach binders. I can save all data in the details foreach.
My Problem
I need to be able to include the value from the contacts foreach binder with the values from the details foreach binder.
What I have tried
I have tried accessig the data from both viewmodels from the parent viewmodel and sending the POST request to the controller from there but the observeableArrays show undefined.
Create.CSHTML (Using MVC5 no razor)
<div id="container1" data-bind="foreach: contacts">
<input type="text" data-bind="value: contactname" />
</div>
<div data-bind="foreach: details" class="card-body">
<input type="text" data-bind="value: itemValue" />
</div>
The save is outside of both foreach binders
<div class="card-footer">
<button type="button" data-bind="click: $root.save" class="btn
btn-success">Send Notification</button>
</div>
<script src="~/Scripts/ViewScripts/ParentVM.js" type="text/javascript"></script>
<script src="~/Scripts/ViewScripts/ViewModel1.js" type="text/javascript"></script>
<script src="~/Scripts/ViewScripts/ViewModel2.js" type="text/javascript"></script>
ViewModel1
var ViewModel1 = function (contacts) {
var self = this;
self.contacts = ko.observableArray(ko.utils.arrayMap(contacts, function (contact) {
return {
contactName: contact.contactName
};
}));
}
ViewModel2
var ViewModel2 = function (details) {
var self = this;
self.details = ko.observableArray(ko.utils.arrayMap(details, function (detail) {
return {
itemNumber: detail.itemValue
};
}));
}
self.save = function () {
$.ajax({
url: baseURI,
type: "POST",
data: ko.toJSON(self.details),
dataType: "json",
contentType: "application/json",
success: function (data) {
console.log(data);
window.location.href = "/Home/Create/";
},
error: function (error) {
console.log(error);
window.location.href = "/Homel/Create/";
}
});
};
ParentViewModel
var VM1;
var VM2;
var initialContactInfo = [
{
contactPhone: ""
}
]
var initialForm = [
{
itemValue: ""
]
}
$(document).ready(function () {
if ($.isEmptyObject(VM1)) {
ArnMasterData = new ViewModel1(initialContactInfo);
ko.applyBindings(VM1, document.getElementById("container1"));
}
if ($.isEmptyObject(VM2)) {
VM2 = new ViewModel2(initialForm);
ko.applyBindings(VM2, document.getElementById("container2"));
}
});

Show form filled data in div when i clicked on submit button

I am new in backbone. I create a form, Now I want to show data in front end with rest service. my code is:
Template:
<script type="text/template" id="details">
<ul>
<% _.each(persons, function(person) { %>
<li><label>emailId : </label><%= person.emailId.emailId %></li>
<li><%= person.emailId.emailId %></li>
<% }); %>
</ul>
</script>
Model , Collection and View
<script type="text/javascript">
var UserModel = Backbone.Model.extend({});
var EntityList = Backbone.Collection
.extend({
model : UserModel,
url : 'http://192.168.1.3:8080/cofinding/business_profile/searchBusiness/123456789'
});
var View = Backbone.View.extend({
el : '#mydiv',
template : _.template($("#details").html()),
initialize : function() {
var self = this;
this.coll = new EntityList();
this.coll.fetch({
success : function() {
self.render();
}
});
},
render : function() {
// the persons will be "visible" in your template
this.$el.html(this.template({
persons : this.coll.toJSON()
}));
return this;
}
});
var view = new View();
</script>
Above code showing my data from service. But I need when I click on submit button.
Let's say you have the following button on your page:
<button id="submit">Submit</button>
Your View will need to define an events object that tracks what happens when a user clicks on your button:
var View = Backbone.View.extend({
events: {
'click #submit': 'fetchEntityList'
},
el: '#myDiv',
//etc...
});
You can then define the function that is executed. It should probably do something similar to what you currently have in initialize:
fetchEntityList: function() {
var self = this;
this.coll.fetch({
success : function() {
self.render();
}
});
}
The fetchEntityList function will now be executed whenever a user clicks Submit. It will fetch your EntityList collection and render it on the page.
hello guys i got my result what i want:
<body>
<div class="container">
<h1>User name</h1>
<hr>
<div class="page"></div>
</div>
<script type="text/template" id="edit-user-template">
<table border="1" cellpadding="4">
<tr>
<th>Email Id</th>
<th>Is Verified</th>
</tr>
<% _.each(users, function(user) { %>
<tr>
<td><%= user.get('emailId').emailId %></td>
<td><%= user.get('emailId').isVerified %></td>
</tr>
<tr>
<td><%= user.get('emailId').emailId %></td>
<td><%= user.get('emailId').isVerified %></td>
</tr>
<% }); %>
</table>
</script>
<script>
var Users = Backbone.Collection
.extend({
url : 'http://192.168.1.3:8080/app/business_profile/searchBusiness/123456789'
});
var UserList = Backbone.View.extend({
el : '.page',
render : function() {
var that = this;
var users = new Users();
users.fetch({
success : function() {
var template = _.template($('#edit-user-template')
.html(), {
users : users.models
});
that.$el.html(template);
}
})
}
});
var Router = Backbone.Router.extend({
routes : {
'' : 'home'
}
});
var userList = new UserList();
var router = new Router();
router.on('route:home', function() {
userList.render();
//console.log('we have loaded the home page');
});
Backbone.history.start();
</script>

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 groupedBy collection is rendered only after one event click

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

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.

Categories