I have a simple single page application which is for my own learning. The page has a UL of all the services that I provide. This list comes from a JSON. Next to each service there is a price for it, and a check-box which the user can select if he needs the service.
Under the list there is a total price value for all the services selected. Based on this, can you please tell me what wrong am I doing? I am trying to integrate the list of services as a handlebar template.
Here is the HTML
<head>
<meta charset="utf-8" />
<title>Your first Backbone.js App | Tutorialzine </title>
<!-- Google web fonts -->
<link href="http://fonts.googleapis.com/css?family=PT+Sans:400,700" rel='stylesheet' />
<!-- The main CSS file -->
<link href="http://localhost/backbone-demo/assets/css/style.css" rel="stylesheet" />
</head>
<body>
<form id="main" method="post" action="submit.php">
<h1>My Services</h1>
<div id="serviceTable"></div>
<ul id="services">
<script id="services-template" type="text/x-handlebars-template">
<!-- The services will be inserted here via handlebars-->
{{#each services}}
<li>
<input type="checkbox" value="1" name="{{title}}"/> {{title}}
<span>${{price}} </span>
</li>
{{/each}}
</script>
</ul>
<p id="total">total: <span>$0</span></p>
<input type="submit" id="order" value="Order" />
</form>
<!-- JavaScript Includes -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://localhost/backbone-demo/assets/js/libs/underscore.min.js"></script>
<script src="http://localhost/backbone-demo/assets/js/libs/backbone.min.js"></script>
<script src="http://localhost/backbone-demo/assets/js/libs/handlebars.js"></script>
<script src="http://localhost/backbone-demo/assets/js/script.js"></script>
</body>
Here is the Javascript:
$(function(){
// Create a model for the services
var Service = Backbone.Model.extend({
// Will contain three attributes.
// These are their default values
defaults:{
title: 'My service',
price: 100,
checked: false
},
// Helper function for checking/unchecking a service
toggle: function(){
this.set('checked', !this.get('checked'));
}
});
// Create a collection of services
var ServiceList = Backbone.Collection.extend({
// Will hold objects of the Service model
model: Service,
// Return an array only with the checked services
getChecked: function(){
return this.where({checked:true});
}
});
//Retrieve the list of services
$.ajax({
type: "GET",
url: "assets/services.json",
async: true,
success: function(response){
// Prefill the collection with a number of services.
var services = new ServiceList(response);
var ServiceView = Backbone.View.extend({
tagName: 'li',
events:{
'click': 'toggleService'
},
initialize: function(){
// Set up event listeners. The change backbone event
// is raised when a property changes (like the checked field)
this.listenTo(services, 'change', this.render);
},
render: function(){
var tpl = Handlebars.compile($("#services-template").html());
//console.log(this.$el.selector);
this.$el.html(tpl({services: services.toJSON()}));
//console.log(this.$el);
$('#serviceTable').append(this.$el);
//document.getElementById('serviceTable').innerHTML = serviceData;
// Create the HTML
/* this.$el.html('<input type="checkbox" value="1" name="' + this.model.get('title') + '" /> ' + this.model.get('title') + '<span>$' + this.model.get('price') + '</span>');
this.$('input').prop('checked', this.model.get('checked')); */
// Returning the object is a good practice
// that makes chaining possible
return this;
},
toggleService: function(){
this.model.toggle();
}
});
// The main view of the application
var App = Backbone.View.extend({
// Base the view on an existing element
el: $('#main'),
initialize: function(){
// Cache these selectors
this.total = $('#total span');
this.list = $('#services');
// Listen for the change event on the collection.
// This is equivalent to listening on every one of the
// service objects in the collection.
this.listenTo(services, 'change', this.render);
// Create views for every one of the services in the
// collection and add them to the page
services.each(function(service){
var view = new ServiceView({ model: service });
this.list.append(view.render().el);
}, this); // "this" is the context in the callback
},
render: function(){
// Calculate the total order amount by agregating
// the prices of only the checked elements
var total = 0;
console.log(services.getChecked());
Handlebars.each(services.getChecked(), function(elem){
console.log(total);
total += elem.get('price');
});
// Update the total price
this.total.text('$'+total);
return this;
}
});
new App();
}
});
});
Below is a workable snippet of your code. You have defined a ServiceView, which is created for each service in your set, however within this view, you are working with the collection as a whole, not this.model and your handlebars template has a for loop in it.
This means that you end up displaying the collection as many times as there are models in the collection. The example below duplicates the list twice as there are 2 records in the list.
There are 2 possible solutions:
Rename your ServiceView to ServicesView and remove the loop within App initialize.
Remove the loop in your template and change the call to this.$el.html(tpl(this.model.toJSON()));
// Create a model for the services
var Service = Backbone.Model.extend({
// Will contain three attributes.
// These are their default values
defaults:{
title: 'My service',
price: 100,
checked: false
},
// Helper function for checking/unchecking a service
toggle: function(){
this.set('checked', !this.get('checked'));
}
});
// Create a collection of services
var ServiceList = Backbone.Collection.extend({
// Will hold objects of the Service model
model: Service,
// Return an array only with the checked services
getChecked: function(){
return this.where({checked:true});
}
});
//Retrieve the list of services
$.ajax({
type: "GET",
url: "http://www.mocky.io/v2/588b429d300000d11afa8d97",
async: true,
success: function(response){
// Prefill the collection with a number of services.
var services = new ServiceList(response);
var ServiceView = Backbone.View.extend({
tagName: 'li',
events:{
'click': 'toggleService'
},
initialize: function(){
// Set up event listeners. The change backbone event
// is raised when a property changes (like the checked field)
this.listenTo(services, 'change', this.render);
},
render: function(){
var tpl = Handlebars.compile($("#services-template").html());
//console.log(this.$el.selector);
this.$el.html(tpl({services: services.toJSON()}));
//console.log(this.$el);
$('#serviceTable').append(this.$el);
//document.getElementById('serviceTable').innerHTML = serviceData;
// Create the HTML
/* this.$el.html('<input type="checkbox" value="1" name="' + this.model.get('title') + '" /> ' + this.model.get('title') + '<span>$' + this.model.get('price') + '</span>');
this.$('input').prop('checked', this.model.get('checked')); */
// Returning the object is a good practice
// that makes chaining possible
return this;
},
toggleService: function(){
this.model.toggle();
}
});
// The main view of the application
var App = Backbone.View.extend({
// Base the view on an existing element
el: $('#main'),
initialize: function(){
// Cache these selectors
this.total = $('#total span');
this.list = $('#services');
// Listen for the change event on the collection.
// This is equivalent to listening on every one of the
// service objects in the collection.
this.listenTo(services, 'change', this.render);
// Create views for every one of the services in the
// collection and add them to the page
services.each(function(service){
var view = new ServiceView({ model: service });
this.list.append(view.render().el);
}, this); // "this" is the context in the callback
},
render: function(){
// Calculate the total order amount by agregating
// the prices of only the checked elements
var total = 0;
console.log(services.getChecked());
Handlebars.each(services.getChecked(), function(elem){
console.log(total);
total += elem.get('price');
});
// Update the total price
this.total.text('$'+total);
return this;
}
});
new App();
}
});
<form id="main" method="post" action="submit.php">
<h1>My Services</h1>
<div id="serviceTable"></div>
<ul id="services">
<script id="services-template" type="text/x-handlebars-template">
<!-- The services will be inserted here via handlebars-->
{{#each services}}
<li>
<input type="checkbox" value="1" name="{{title}}"/> {{title}}
<span>${{price}} </span>
</li>
{{/each}}
</script>
</ul>
<p id="total">total: <span>$0</span></p>
<input type="submit" id="order" value="Order" />
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.6/handlebars.min.js"></script>
Related
I've been trying to get my first Backbone.js app up and running, following the Backbone.js primer here.
I've followed the example through and now I'm trying to customise it for my purposes which are to simply retrieve and read a JSON file from my server. I don't need to be able to change or delete any of the data.
I've set up my html as per the primer below:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Backbone.js Primer</title>
<script type="text/javascript" src="./node_modules/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="./node_modules/underscore/underscore-min.js"></script>
<script type="text/javascript" src="./node_modules/backbone/backbone-min.js"></script>
<script type="text/javascript" src="./node_modules/moment/moment.js"></script>
<script type="text/javascript" src="./backbone.js"></script>
</head>
<body>
<div>
<h1>Transcripts Data</h1>
<div id="dailyTranscripts-app">
<ul class="dailyTranscripts-list"></ul>
</div>
</div>
</body>
</html>
I've then coded my backbone.js file as the primer describes below:
var yesterday = moment (new Date()).add(-1, 'days').format('YYYY-MM-DD')
var yesterdaysDataURL = 'https://mihndbotblob.blob.core.windows.net/mihndbot-transcripts/finalTranscripts/dailyTranscripts/' + yesterday + '.json'
// Model class for each transcript iten
var DailyTranscriptsModel = Backbone.Model.extend({
defaults: {
type: null,
MessageID: null,
MessageTime: null,
MessageChannel: null,
MessageSenderID: null,
MessageSenderName: null,
ConversationID: null,
MessageText: null,
MessageRecipientID: null,
QuickReplyDisplayText: null,
QuickReplyPayload: null,
Question: null,
Answer: null,
FollowUpPrompts: null
}
});
// Collection class for the DailyTransctipts list endpoint
var DailyTranscriptsCollection = Backbone.Collection.extend({
model: DailyTranscriptsModel,
url: yesterdaysDataURL
});
// View class for displaying each dailyTranscripts list item
var DailyTranscriptsListItemView = Backbone.View.extend({
tagName: 'li',
className: 'dailyTranscripts',
initialize: function () {
this.listenTo(this.model)
},
render: function () {
var html = '<b>Message ID: </b> ' + this.model.get('MessageID');
html += '<br><b>Message Time: </b>' + this.model.get('MessageTime');
this.$el.html(html);
return this;
}
});
// View class for rendering the list of all dailyTranscripts
var DailyTranscriptsListView = Backbone.View.extend({
el: '#dailyTranscripts-app',
initialize: function () {
this.listenTo(this.collection, 'sync', this.render);
},
render: function () {
var $list = this.$('ul.dailyTranscripts-list').empty();
this.collection.each(function (model) {
var item = new DailyTranscriptsListItemView({model: model});
$list.append(item.render().$el);
}, this);
return this;
}
});
// Create a new list collection, a list view, and then fetch list data:
var dailyTranscriptsList = new DailyTranscriptsCollection();
var dailyTranscriptsView = new DailyTranscriptsListView({collection: dailyTranscriptsList });
dailyTranscriptsList.fetch();
The major changes I've made to the code (apart from some customisations) are to remove the templates the primer uses to create the views (I couldn't get them working) and I've removed the Backbone CRUD elements as I only require my app to read data from the server, not update or delete it.
The issue I have is that whilst I'm pulling back the JSON file from the server, none of the data is rendering in the HTLM <div> as expected, it's just blank.
I know that Backbone.js is retrieving the data as when I add .then(function() {console.log(dailyTranscriptsList);}); to the final dailyTranscriptsList.fetch() call I can see the data in the browser console:
You need to wrap all of your backbone.js code within jQuery's .ready()
// backbone.js
$(document).ready(function () {
// all your backbone.js code here
})
This causes your js to run after the DOM is ready, so Backbone will know how to find the elements it needs in order for views to work.
You could also move <script type="text/javascript" src="./backbone.js"></script> to the end of the page, right before </body>
I'm using python to create a dummy server storing JSON data. I'm trying to fetch the data to display it in a dashboard. I keep getting
cannot read property html of undefined
and
cannot read property render of undefined
What am I missing?
My backbone script:
// Create a Model
var Dashboard = Backbone.Model.extend({});
// Create a collection
var DashboardCollection = Backbone.Collection.extend({
model: Dashboard,
url: 'http://localhost:8889/api/test'
});
// create an instance of the collection object
var jobList = new DashboardCollection();
jobList.fetch({success:function(){
test.render();
}});
// Create a jobList view
var jobListView= Backbone.View.extend({
el: $('.jobsList'),
template: _.template($('#test-template').html()),
initialize: function(){
this.render();
//this.listenTo(this.model, 'change', this.render);
//this.listenTo(this.model, 'destroy', this.remove);
},
render : function(){
this.$el.html(this.template({'last_name':'test'}));
return this;
}
});
var test = new jobListView;
And my HTML:
<main>
<div class="row">
<div class="left glass">
<!--[if lt IE 9]>
<div class="legacy-ie-fix"></div>
<![endif]-->
<h1>Job List</h1>
<div class ="jobsList">
</div>
</div>
<div class="right glass">
<!--[if lt IE 9]>
<div class="legacy-ie-fix"></div>
<![endif]-->
<h1>Metrics</h1>
<div id="metrics">
<div class="row">
</div>
</div>
</div>
</div>
</main>
</body>
<script type="text/template" id="test-template">
<table class="table striped">
<thead>
<tr>
<th>Data</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= last_name %></td>
</tr>
</tbody>
</table>
</script>
It seems to be an ordering problem.
Make sure the document is ready
If you use jQuery in your script to grab an element from the document (like el: $('.jobsList')), you must ensure that the HTML is ready. You can wrap your code in a jQuery style document ready function:
$(function() {
var JobListView = Backbone.View.extend({
el: $('.jobsList'),
template: _.template($('#test-template').html()),
render: function() {
this.$el.html(this.template({ 'last_name': 'test' }));
return this;
}
});
});
Or just load the scripts at the bottom of the <body> but inside of it.
<script type="text/template" id="test-template">
Put the template above the scripts loading and inside the body.
</script>
<script src="jquery.js">
<script src="underscore.js">
<script src="backbone.js">
<script src="script/my-app.js">
</body>
The order of the <script> tags on the page is important. Backbone requires jQuery and Underscore.js to be loaded before and your own code requires Backbone (and jQuery, but that's already taken care of in the dependency chain).
Declare and assign variable before using them
You call fetch on the collection, which uses the view before it is assigned. While it could work (see var hoisting), it's best to declare and assign variables before using them when possible.
// Create a list view class
var JobListView = Backbone.View.extend({
el: '.jobsList', // no need to use jQuery here.
template: _.template($('#test-template').html()),
render: function() {
this.$el.html(this.template({ 'last_name': 'test' }));
return this;
}
});
// instantiate the view first.
var test = new JobListView();
// then create an instance of the collection object
var jobList = new DashboardCollection();
// and fetch it when everything is ready.
jobList.fetch({
success: function() {
test.render();
}
});
Notice that JS custom types (classes) should be in PascalCase rather than in snakeCase as a generally approved standard, but that's not going to make the code fail.
Pass the element to the view
To be able to easily reuse your views within different views and templates, you should avoid hard-coding the el property.
Instead, pass the element to the view:
var JobListView = Backbone.View.extend({
// ...
});
// ...somewhere else
var view = new JobListView({ el: '.jobsList' });
Or use the element created by a Backbone view.
var JobListView = Backbone.View.extend({
className: 'jobList',
});
// ...inside a parent view's render
var ParentView = Backbone.View.extend({
template: '<div class="job-list-1"></div><div class="job-list-2"></div>',
render: function() {
this.$el.html(this.template);
this.$('.job-list-1').html(new JobListView().render().el);
this.$('.job-list-2').html(new JobListView().render().el);
// ...
return this;
}
});
This would result in:
<div class="job-list-1">
<div class="jobList"></div>
</div>
<div class="job-list-2">
<div class="jobList"></div>
</div>
I am working on a project in Backbone.js were I get the results from a Food API and then display them. I have this piece of functionality working. The next piece of functionality I need is to be able to click an item from the results list and be able to save that result, showing it in the foods tracked list on the right side of the page. The foods tracked list would show the information about the Food (Food Name, Brand and Calories) as well as a total amount of calories from all the foods tracked. I am having trouble creating this functionality because I do not know how to click a list item and have it take the item information in the html list element and place it in another part of the page.
Here is my JSfiddle link- https://jsfiddle.net/Tiquismiquis/2nLezvmg/3/
Here is my JAVASCRIPT-
$(function(){
var SearchList = Backbone.Collection.extend({
initialize: function(){
this.bind("reset", function(model, options){
console.log("Inside event");
console.log(model);
});
},
//** 1. Function "parse" is a Backbone function to parse the response properly
parse:function(response){
//** return the array inside response, when returning the array
//** we left to Backone populate this collection
return response.hits;
}
});
// The main view of the application
var App = Backbone.View.extend({
el: 'body',
events: {
"input #searchBox" : "prepCollection",
"click li" : "track"
},
initialize: function () {
this.model = new SearchList();
this.prepCollection =_.debounce(this.prepCollection, 1000);
this.$list = $('#listing');
// this.saved =$('#tracked');
},
prepCollection: function(){
var name = $('input').val();
var newUrl = "https://api.nutritionix.com/v1_1/search/" + name + "?results=0%3A20&cal_min=0&cal_max=50000&fields=item_name,brand_name,item_id,nf_calories&appId=26952a04&appKey=private_key";
if (name == ""){
this.$list.html("")
}
else{
this.model.url = newUrl;
this.model.fetch({
success: function (response, xhr) {
console.log("Inside success");
console.log(response.toJSON());
},
error: function (errorResponse) {
console.log(errorResponse)
}
});
this.listenTo(this.model, 'sync', this.render);
}
},
// track: function(){
// },
render: function(){
var terms = this.model;
var wordhtml = "";
terms.each(function (term) {
wordhtml = wordhtml + "<li>" +"<strong>" + term.get('fields')["item_name"] + '</strong>'+ ' ('+ term.get('fields')["brand_name"] + ')'+' - '+ term.get('fields')["nf_calories"] + ' Calories' + "</li>"
}, this);
this.$list.html(wordhtml);
}
});
var app = new App();
});
Here is my HTML-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Food Guide App</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-6">
<h1>Interactive Food Guide</h1>
<input type="text" id="searchBox"> <br/><br/>
<ul id="listing"></ul>
</div>
<div class="col-xs-6">
<h1>Foods Tracked</h1>
<ul id="tracked"></ul>
<p id="total">total calories: <span>0</span></p>
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Backbone and Underscore -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.1/backbone-min.js"></script>
<!-- apps functionality -->
<script src="js/app.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>
There are many ways to do it - through a backbone listView or simply by adding data-* attributes to the element.
Below is an example demonstrating the latter :
Template change :
var liTemplate = '<li data-brand="<%-data.brand_name%>" data-name="<%-data.item_name%>"><strong><%-data.item_name%> (<%-data.brand_name%>)</strong></li>';
wordhtml = _.template(liTemplate)({ data : term.get('fields')});
View change :
events:{
'click li': 'track'
},
track: function(e){
var $target = $(e.currentTarget);
var itemName = $target.attr('data-name');
var brandName = $target.attr('data-brand');
//do whatever you need
}
Find the working fiddle at https://jsfiddle.net/nitincool4urchat/2nLezvmg/8/
i am trying to learn backbone.js ( Backbone.js 1.0.0) this is my sample html page where iam using collection. fetch() method to get the collection,and it is displayed using view .i am getting result in
google chrome,but nothing is displayed in mozilla. i don't know the exact reason.
while i refere to backone site http://backbonejs.org/#Collection-fetch
it is qouted that :
Note that fetch should not be used to populate collections on page load — all models needed at load time should already be bootstrapped in to place. fetch is intended for lazily-loading models for interfaces that are not needed immediately: for example, documents with collections of notes that may be toggled open and closed.
is this is related with my issue?
this is my sample html page
<!DOCTYPE html>
<html>
<head>
<title>Backbone Application</title>
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/underscore.js" type="text/javascript"></script>
<script src="js/backbone.js" type="text/javascript"></script>
</head>
<body>
<div class="list"></div>
<script id="personTemplate" type="text/template">
<td> <strong><%= name %></strong></td>
<td>(<%= age %>) </td>
<td> <%= occupation %> </td>
</script>
<script type="text/javascript">
//Person Model
var Person = Backbone.Model.extend({
defaults: {
name: 'Guest User',
age: 30,
occupation: 'worker'
}
});
// A List of People
var PeopleCollection = Backbone.Collection.extend({
model: Person,
initialize: function(){
alert("intialise")
},
url:'/RestFul/rest/members/info',
});
// View for all people
var PeopleView = Backbone.View.extend({
tagName: 'table',
render: function(){
this.collection.each(function(person){
var personView = new PersonView({ model: person });
this.$el.append(personView.render().el); // calling render method manually..
}, this);
return this; // returning this for chaining..
}
});
// The View for a Person
var PersonView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#personTemplate').html()),
////////// initialize function is gone from there. So we need to call render method manually now..
render: function(){
this.$el.html( this.template(this.model.toJSON()));
return this; // returning this from render method..
}
});
var peopleCollection = new PeopleCollection();
//peopleCollection.fetch();
peopleCollection.fetch({ success: function () { console.log("collection fetched"); } });
//peopleCollection.fetch({context:collection}).done(function() {
// console.log(this.length)
// })
//console.log(peopleCollection.toJSON())
alert(JSON.stringify(peopleCollection));
var peopleView = new PeopleView({ collection: peopleCollection });
$(document.body).append(peopleView.render().el); // adding people view in DOM
</script>
</body>
</html>
any help will be appreciated
Try with
var fetching = peopleCollection.fetch({ success: function () { console.log("collection fetched"); } });
$.when(fetching).done(function(){
var peopleView = new PeopleView({ collection: peopleCollection });
$(document.body).append(peopleView.render().el); // adding people view in DOM
});
var fetching = peopleCollection.fetch({ success: function () {
var peopleView = new PeopleView({ collection: peopleCollection });
$(document.body).append(peopleView.render().el);
} });
I think we can call the view render inside the success callback
I just start using Backbone. I have two questions to ask based on the code below.
The first issue is after I fill out the form and click the button, the model object should be created with some default attributes. However, the console.log prints the model with the newest attribute from the form I fill out before I pass the model to the new view object.
The second issue is I can successfully save the data to db, but my success call back function is not being called. could someone help me to answer these questions??
var form = document.forms[0];
var RetailerModel = Backbone.Model.extend({
urlRoot: ' retailer.php',
defaults: {
name: 'company-name',
address: 'company-address',
phone: 'company-phone',
icon: 'http://localhost/icon.png'
}
});
var RetailerCollection = Backbone.Collection.extend({
});
var RetailerView = Backbone.View.extend({
className: 'retailer',
template: _.template($('#retailer-template').html()),
initialize: function() {
//this.listenTo(this.model, 'change', this.render);
var obj = {
name: form.name.value,
address: form.address.value,
phone: form.phone.value
};
this.model.set(obj);
//why the successful callback does not work????
this.model.save(null, {success: function(model, response){console.log('successful');}});
},
render: function() {
$('#retailer-list').append(this.$el.html(this.template(this.model.toJSON())));
return this;
}
});
var RetailerViews = Backbone.View.extend({
});
$('#submit').click(function(e){
var retailer_model = new RetailerModel();
console.log(retailer_model); // this prints out the new changed attributes instead of the default ones, why???
var retailer_view = new RetailerView({model: retailer_model});
form.reset();
});
1 Get the correct state of the model
console.log(retailer_model) will not show the models attributes, it'll show the whole model, but console.log(retailer_model.attributes) will. Also bear in mind that console.log isn't always on correct, especially if you modify the object just after you log it this can lead to confusion!
To get the actual current state of the model you should make a shallow or deep copy of it. So rather than console.log(model) you could use underscores clone method for a shallow copy:
console.log(_(model).clone());
2 Success Callback on Save
To help there we'd really need to know more about your circumstances. The first thing I'd look for is whether your server is returning the right feedback. Backbone is a system based on REST standards. Are you sure your server is returning the right response code? If you use chrome, open your developer tools and then the network tab to inspect what response you're getting when posting your model. In order for the success callback to be fired the server should return a status of 200 or 201.
Another way to test that is to see whether the error callback is firing :).
I tried to see your errors, I realized that you missed these things:
1. You should do DOM related things when DOM ready event fired:
$(function() {
form = document.forms[0];
$(form).live('submit', function(e){
// ...
});
});
2. You can use submit for forms. It catches also enter click within form:
$(form).live('submit',function(){
//...
});
3. You should use return false; within submit form. It prevents default form data sending to action url.
$(form).live('submit', function(e){
// ...
return false;
});
So, it looks like this. I didn't check success callback, but I hope it will work.
<html>
<head>
<script src="jquery-1.7.2.js" type="text/javascript"></script>
<script type="text/javascript" src="underscore.js"></script>
<script type="text/javascript" src="backbone.js"></script>
<script type="text/javascript">
var RetailerModel = Backbone.Model.extend({
urlRoot: ' retailer.php',
defaults: {
name: 'company-name',
address: 'company-address',
phone: 'company-phone',
icon: 'http://localhost/icon.png'
}
});
var RetailerCollection = Backbone.Collection.extend({});
var RetailerView = Backbone.View.extend({
className: 'retailer',
//template: _.template($('#retailer-template').html()),
initialize: function() {
//this.listenTo(this.model, 'change', this.render);
var obj = {
name: form.name.value,
address: form.address.value,
phone: form.phone.value
};
this.model.set(obj);
//why the successful callback does not work????
this.model.save(null, {
success: function(model, response){
console.log('successful');
}
});
},
render: function() {
$('#retailer-list').append(this.$el.html(this.template(this.model.toJSON())));
return this;
}
});
var RetailerViews = Backbone.View.extend({});
$(function() { // you should do DOM related things when DOM ready event fired
form = document.forms[0];
$(form).live('submit', function(e){
var retailer_model = new RetailerModel();
console.log(retailer_model);
var retailer_view = new RetailerView({model: retailer_model});
form.reset();
return false; // to prevent form data sending return false
});
});
</script>
</head>
<body>
<form action="#" id="#submit">
<input type="submit" value="submit"/>
<input type="text" name="address"/>
<input type="text" name="name"/>
<input type="text" name="phone"/>
</form>
</body>
</html>