Fixed a bug with the updater.

This commit is contained in:
Elf M. Sternberg 2011-08-07 17:44:17 -07:00
parent e7625991a1
commit 58c3d50bce
5 changed files with 162 additions and 135 deletions

View File

@ -39,7 +39,7 @@ store.js: backbonestore.nw
rm $*.nw-tmp; \ rm $*.nw-tmp; \
fi fi
index.html: index.html: backbonestore.nw
@ $(ECHO) $(NOTANGLE) -c -R$@ $< @ $(ECHO) $(NOTANGLE) -c -R$@ $<
@ - $(NOTANGLE) -c -R$@ $< > $*.nw-html-tmp @ - $(NOTANGLE) -c -R$@ $< > $*.nw-html-tmp
@ if [ -s "$*.nw-html-tmp" ]; then \ @ if [ -s "$*.nw-html-tmp" ]; then \

View File

@ -144,9 +144,11 @@ Also, it would be nice to know the total price of the Item.
<<cart models>>= <<cart models>>=
var Item = Backbone.Model.extend({ var Item = Backbone.Model.extend({
update: function(amount) { update: function(amount) {
this.set({'quantity': amount}); this.set({'quantity': amount}, {silent: true});
this.collection.trigger('change', this);
}, },
price: function() { price: function() {
console.log(this.get('product').get('title'), this.get('quantity'));
return this.get('product').get('price') * this.get('quantity'); return this.get('product').get('price') * this.get('quantity');
} }
}); });
@ -236,7 +238,7 @@ and remove tabs from the top of the viewport as needed.
<<base view>>= <<base view>>=
var _BaseView = Backbone.View.extend({ var _BaseView = Backbone.View.extend({
parent: '#main', parent: $('#main'),
className: 'viewport', className: 'viewport',
@ @
@ -313,8 +315,8 @@ for our one-page application:
</title> </title>
<link rel="stylesheet" href="jsonstore.css" type="text/css"> <link rel="stylesheet" href="jsonstore.css" type="text/css">
<<product list template>> <<product list template>>
<<product template>> <<product detail template>>
<<checkout template>> <<cart template>>
</head> </head>
<body> <body>
<div id="container"> <div id="container">
@ -388,23 +390,23 @@ you should understand this fairly easily.
And here is the HTML: And here is the HTML:
<<product list template>>= <<product list template>>=
<script id="store_index_template" type="text/x-underscore-tmplate"> <script id="store_index_template" type="text/x-underscore-tmplate">
<h1>Product Catalog</h1> <h1>Product Catalog</h1>
<ul> <ul>
<% for(i=0,l=products.length;i<l;++i) { p = products[i]; %> <% for(i=0,l=products.length;i<l;++i) { p = products[i]; %>
<li class="item"> <li class="item">
<div class="item-image"> <div class="item-image">
<a href="#item/<%= p.id %>"> <a href="#item/<%= p.id %>">
<img alt="<%= p.title %>" src="<%= p.image %>" /> <img alt="<%= p.title %>" src="<%= p.image %>" />
</a> </a>
</div> </div>
</li> <div class="item-artist"><%= p.artist %></div>
<div class="item-artist"><%= p.artist %></div> <div class="item-title"><%= p.title %></div>
<div class="item-title"><%= p.title %></div> <div class="item-price">$<%= p.price %></div>
<div class="item-price">$<%= p.price %></div> </li>
<% } %> <% } %>
</ul> </ul>
</script> </script>
@ @
%$ %$
@ -467,12 +469,20 @@ get its id and so forth. All of that has been put into the shopping
cart model, which is where it belongs: \textit{knowledge about items cart model, which is where it belongs: \textit{knowledge about items
and each item's relationship to its collection belongs in the and each item's relationship to its collection belongs in the
collection}. collection}.
Look closely at the [[update()]] method. The reference [[this.\$]] is
a special Backbone object that limits selectors to objects inside the
element of the view. Without it, jQuery would have found the first
input field of class 'uqf' in the DOM, not the one for this specific
view. [[this.\$('.uqf')]] is shorthand for [[$('uqf', this.el)]], and
helps clarify what it is you're looking for.
%' %'
<<product detail view>>= <<product detail view>>=
update: function(e) { update: function(e) {
e.preventDefault(); e.preventDefault();
this.item.update(parseInt($('.uqf').val())); this.item.update(parseInt(this.$('.uqf').val()));
}, },
updateOnEnter: function(e) { updateOnEnter: function(e) {
@ -499,33 +509,33 @@ The product detail template is fairly straightforward. There is no
[[underscore]] magic because there are no loops. [[underscore]] magic because there are no loops.
<<product detail template>>= <<product detail template>>=
<script id="store_item_template" type="text/x-underscore-template"> <script id="store_item_template" type="text/x-underscore-template">
<div class="item-detail"> <div class="item-detail">
<div class="item-image"> <div class="item-image">
<img alt="<%= title %>" src="<%= large_image %>" /> <img alt="<%= title %>" src="<%= large_image %>" />
</div> </div>
</div> </div>
<div class="item-info"> <div class="item-info">
<div class="item-artist"><%= artist %></div> <div class="item-artist"><%= artist %></div>
<div class="item-title"><%= title %></div> <div class="item-title"><%= title %></div>
<div class="item-price">$<%= price %></div> <div class="item-price">$<%= price %></div>
<form action="#/cart" method="post"> <form action="#/cart" method="post">
<p> <p>
<label>Quantity:</label> <label>Quantity:</label>
<input class="uqf" name="quantity" size="2" type="text" value="1" /> <input class="uqf" name="quantity" size="2" type="text" value="1" />
</p> </p>
<p> <p>
<input class="uq" type="submit" value="Add to Cart" /> <input class="uq" type="submit" value="Add to Cart" />
</p> </p>
</form> </form>
<div class="item-link"> <div class="item-link">
<a href="<%= url %>">Buy this item on Amazon</a> <a href="<%= url %>">Buy this item on Amazon</a>
</div> </div>
<div class="back-link"> <div class="back-link">
<a href="#">&laquo; Back to Items</a> <a href="#">&laquo; Back to Items</a>
</div> </div>
</div> </div>
</script> </script>
@ @
So, let's talk about that shopping cart thing. We've been making the So, let's talk about that shopping cart thing. We've been making the
@ -570,9 +580,9 @@ show that it did changed:
And the HTML for the template is dead simple: And the HTML for the template is dead simple:
<<cart template>>= <<cart template>>=
<script id="store_cart_template" type="text/x-underscore-template"> <script id="store_cart_template" type="text/x-underscore-template">
<p>Items: <%= count %> ($<%= cost %>)</p> <p>Items: <%= count %> ($<%= cost %>)</p>
</script> </script>
@ @
%$ %$
@ -722,6 +732,8 @@ Here's the entirety of the program:
<<product detail view>> <<product detail view>>
<<cart widget>>
<<router>> <<router>>
<<initialization>> <<initialization>>

View File

@ -1,73 +1,75 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<title>The Backbone Store</title> <title>
<link charset="utf-8" href="jsonstore.css" rel="stylesheet" type="text/css" /> The Backbone Store
</title>
<script id="store_index_template" type="text/x-underscore-tmplate"> <link rel="stylesheet" href="jsonstore.css" type="text/css">
<h1>Product Catalog</h1> <script id="store_index_template" type="text/x-underscore-tmplate">
<ul> <h1>Product Catalog</h1>
<% for(i=0,l=products.length;i<l;++i) { p = products[i]; %> <ul>
<li class="item"> <% for(i=0,l=products.length;i<l;++i) { p = products[i]; %>
<div class="item-image"> <li class="item">
<a href="#item/<%= p.id %>"> <div class="item-image">
<img alt="<%= p.title %>" src="<%= p.image %>" /> <a href="#item/<%= p.id %>">
</a> <img alt="<%= p.title %>" src="<%= p.image %>" />
</a>
</div>
<div class="item-artist"><%= p.artist %></div>
<div class="item-title"><%= p.title %></div>
<div class="item-price">$<%= p.price %></div>
</li>
<% } %>
</ul>
</script>
<script id="store_item_template" type="text/x-underscore-template">
<div class="item-detail">
<div class="item-image">
<img alt="<%= title %>" src="<%= large_image %>" />
</div>
</div> </div>
</li> <div class="item-info">
<div class="item-artist"><%= p.artist %></div> <div class="item-artist"><%= artist %></div>
<div class="item-title"><%= p.title %></div> <div class="item-title"><%= title %></div>
<div class="item-price">$<%= p.price %></div> <div class="item-price">$<%= price %></div>
<% } %> <form action="#/cart" method="post">
</ul> <p>
</script> <label>Quantity:</label>
<input class="uqf" name="quantity" size="2" type="text" value="1" />
</p>
<p>
<input class="uq" type="submit" value="Add to Cart" />
</p>
</form>
<div class="item-link">
<a href="<%= url %>">Buy this item on Amazon</a>
</div>
<div class="back-link">
<a href="#">&laquo; Back to Items</a>
</div>
</div>
</script>
<script id="store_cart_template" type="text/x-underscore-template">
<p>Items: <%= count %> ($<%= cost %>)</p>
</script>
<script id="store_item_template" type="text/x-underscore-template"> </head>
<div class="item-detail"></div> <body>
<div class="item-image"> <div id="container">
<img alt="<%= title %>" src="<%= large_image %>" /> <div id="header">
</div> <h1>
<div class="item-info"></div> The Backbone Store
<div class="item-artist"><%= artist %></div> </h1>
<div class="item-title"><%= title %></div>
<div class="item-price">$<%= price %></div>
<div class="item-form"></div>
<form action="#/cart" method="post">
<p>
<label>Quantity:</label>
<input class="uqf" name="quantity" size="2" type="text" value="1" />
</p>
<p>
<input class="uq" type="submit" value="Add to Cart" />
</p>
</form>
<div class="item-link">
<a href="<%= url %>">Buy this item on Amazon</a>
</div>
<div class="back-link">
<a href="#">&laquo; Back to Items</a>
</div>
</script>
<script id="store_cart_template" type="text/x-underscore-template"> <div class="cart-info">
<p>Items: <%= count %> ($<%= cost %>)</p> </div>
</script> </div>
</head> <div id="main"> </div>
</div>
<body> <script src="jquery-1.6.2.min.js" type="text/javascript"></script>
<div id="container"> <script src="underscore.js" type="text/javascript"></script>
<div id="header"> <script src="backbone.js" type="text/javascript"></script>
<h1> <script src="store.js" type="text/javascript"></script>
The Backbone Store </body>
</h1>
<div class="cart-info"></div>
</div>
<div id="main"></div>
</div>
<script src="jquery-1.6.2.min.js" type="text/javascript"></script>
<script src="underscore.js" type="text/javascript"></script>
<script src="backbone.js" type="text/javascript"></script>
<script src="store.js" type="text/javascript"></script>
</body>
</html> </html>

View File

@ -33,12 +33,17 @@ body {
img { img {
border: 0; border: 0;
} }
#productlistview ul {
list-style: none;
}
.item { .item {
display: border;
float:left; float:left;
width: 250px; width: 250px;
margin-right: 3px; margin-right: 10px;
padding: 2px; margin-bottom: 10px;
padding: 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
text-align:center; text-align:center;
font-size: 12px; font-size: 12px;

View File

@ -16,12 +16,18 @@
var Item = Backbone.Model.extend({ var Item = Backbone.Model.extend({
update: function(amount) { update: function(amount) {
this.set({'quantity': this.get('quantity') + amount}); this.set({'quantity': amount}, {silent: true});
this.collection.trigger('change', this);
},
price: function() {
console.log(this.get('product').get('title'), this.get('quantity'));
return this.get('product').get('price') * this.get('quantity');
} }
}); });
var ItemCollection = Backbone.Collection.extend({ var ItemCollection = Backbone.Collection.extend({
model: Item, model: Item,
getOrCreateItemForProduct: function(product) { getOrCreateItemForProduct: function(product) {
var i, var i,
pid = product.get('id'), pid = product.get('id'),
@ -35,18 +41,19 @@
this.add(i, {silent: true}) this.add(i, {silent: true})
return i; return i;
}, },
getTotalCount: function() { getTotalCount: function() {
return this.reduce(function(memo, obj) { return this.reduce(function(memo, obj) {
return obj.get('quantity') + memo; }, 0); return obj.get('quantity') + memo; }, 0);
}, },
getTotalCost: function() { getTotalCost: function() {
return this.reduce(function(memo, obj) { return this.reduce(function(memo, obj) {
return (obj.get('product').get('price') * return obj.price() + memo; }, 0);
obj.get('quantity')) + memo; }, 0);
} }
}); });
var _BaseView = Backbone.View.extend({ var _BaseView = Backbone.View.extend({
parent: $('#main'), parent: $('#main'),
className: 'viewport', className: 'viewport',
@ -63,9 +70,8 @@
return null; return null;
} }
promise = $.Deferred(_.bind(function(dfd) { promise = $.Deferred(_.bind(function(dfd) {
this.el.fadeOut('fast', dfd.resolve)}, this)).promise(); this.el.fadeOut('fast', dfd.resolve)}, this));
this.trigger('hide', this); return promise.promise();
return promise;
}, },
show: function() { show: function() {
@ -73,13 +79,12 @@
return; return;
} }
promise = $.Deferred(_.bind(function(dfd) { promise = $.Deferred(_.bind(function(dfd) {
this.el.fadeIn('fast', dfd.resolve) }, this)).promise(); this.el.fadeIn('fast', dfd.resolve) }, this))
return promise.promise();
this.trigger('show', this);
return promise;
} }
}); });
var ProductListView = _BaseView.extend({ var ProductListView = _BaseView.extend({
id: 'productlistview', id: 'productlistview',
template: $("#store_index_template").html(), template: $("#store_index_template").html(),
@ -96,6 +101,7 @@
} }
}); });
var ProductView = _BaseView.extend({ var ProductView = _BaseView.extend({
id: 'productitemview', id: 'productitemview',
template: $("#store_item_template").html(), template: $("#store_item_template").html(),
@ -113,7 +119,7 @@
update: function(e) { update: function(e) {
e.preventDefault(); e.preventDefault();
this.item.update(parseInt($('.uqf').val())); this.item.update(parseInt(this.$('.uqf').val()));
}, },
updateOnEnter: function(e) { updateOnEnter: function(e) {
@ -128,6 +134,7 @@
} }
}); });
var CartWidget = Backbone.View.extend({ var CartWidget = Backbone.View.extend({
el: $('.cart-info'), el: $('.cart-info'),
template: $('#store_cart_template').html(), template: $('#store_cart_template').html(),
@ -137,7 +144,6 @@
}, },
render: function() { render: function() {
console.log(arguments);
this.el.html( this.el.html(
_.template(this.template, { _.template(this.template, {
'count': this.collection.getTotalCount(), 'count': this.collection.getTotalCount(),
@ -147,6 +153,7 @@
} }
}); });
var BackboneStore = Backbone.Router.extend({ var BackboneStore = Backbone.Router.extend({
views: {}, views: {},
products: null, products: null,
@ -200,4 +207,5 @@
new BackboneStore(); new BackboneStore();
Backbone.history.start(); Backbone.history.start();
}); });
}).call(this); }).call(this);