AngularJS Shopping List Widget is an AngularJS widget that allows a user to manually enter items of a shopping list, along with prices and coupons, and then print it.
A Pen by Alex Whapham on CodePen.
AngularJS Shopping List Widget is an AngularJS widget that allows a user to manually enter items of a shopping list, along with prices and coupons, and then print it.
A Pen by Alex Whapham on CodePen.
<div class="wrapper" ng-app="shoppingListApp"> | |
<div class="box"> | |
<h1>AngularJS Shopping List Widget</h1> | |
<p>AngularJS Shopping List Widget is an AngularJS widget that allows a user to manually enter items of a shopping list, along with prices and coupons, and then print it.</p> | |
<p>It runs entirely in AngularJS, and updates in real time.</p> | |
<div class="demoWrapper" ng-controller="listController"> | |
<h2>Demo</h2> | |
<div class="shoppingList"> | |
<h3>Your Shopping List</h3> | |
<ul> | |
<li ng-repeat="item in listItems"> | |
<div class="listItem">{{item.description}}</div> | |
<div class="listPrice">${{item.price}}</div> | |
<div class="closeListItem"><a ng-click="maximizeCouponEnter(item)"><img src="https://cdn4.iconfinder.com/data/icons/kitchen-cooking-dining/256/coupon-512.png" height="20"><a ng-click="removeListItem(item)">x</a></div> | |
<ul class="enterCoupon"> | |
<li> | |
<input type="text" placeholder="Coupon Description/Name" class="newCouponName" ng-model="newCouponDesc"> | |
<input type="text" placeholder="Discount" class="newCouponPrice" ng-model="newCouponDiscount"> | |
<a class="addCouponButton" ng-click="addCoupon(item, newCouponDesc, newCouponDiscount)">Add</a> | |
<a class="minimize" ng-click="minimizeCouponEnter(item)">–</a> | |
</li> | |
</ul> | |
<ul class="couponsList"> | |
<li class="couponItem" ng-repeat="coupon in item.coupons"> | |
<div class="thisCouponDescription"> | |
{{ coupon.name }} </div> | |
<div class="thisCouponDiscount">-${{ coupon.discount }}<a ng-click="removeCoupon(item, coupon)">x</a></div> | |
</li> | |
</ul> | |
</li> | |
</ul> | |
</div> | |
<div class="addItemModule"> | |
<h3>Add an Item</h3> | |
<label class="addItemDesc">Description</label> | |
<form name="addForm"> | |
<input type="text" class="addItemTBox" placeholder="Enter a product" ng-model="newDescription"> | |
<label class="addItemPrice">Price</label> | |
<input type="text" class="addItemTBox" placeholder="Enter price" ng-model="newPrice"> | |
<a ng-click="addListItem(newDescription, newPrice)">Add Item</a> | |
</form> | |
<p>Don't worry about adding coupons here, you'll have an opportunity to do that once you add the item to your list and a coupon clip icon appears next to it.</p> | |
<label class="totalPrice">Total Cost(Before Tax)</label> | |
<input type="text" ng-model="totalBeforeTax" class="addItemTBox" disabled> | |
<label class="stateTaxLabel">State Tax</label> | |
<select ng-model="taxState" class="taxState" id="taxStateSelected" ng-options="s.name for s in states" ng-change="toggleOtherTax(taxState.tax)"> | |
</select> | |
<div id="OtherTax"> | |
<label>Other Tax</label> | |
<input type="text" class="addItemTBox" id="otherTaxBox" ng-model="otherTax" ng-keyup="addOtherTax()"> | |
</div> | |
<p>If you do not live in the U.S., or the tax of your jurisdiction is the U.S. is different from that of your state, please select "Other" from above and a box will appear below it to allow you to input your own tax.</p> | |
<label class="totalPricePlusTax">Total Cost(After Tax)</label> | |
<input ng-model="costAfterTax" type="text" class="addItemTBox" disabled> | |
<label class="totalPricePlusTax">Total Coupon Savings</label> | |
<input type="text" ng-model="couponSavings" class="addItemTBox" disabled> | |
<label class="totalPricePlusTax">Final Price</label> | |
<input type="text" class="addItemTBox" ng-model="finalPriceModel" disabled> | |
</div> | |
</div> | |
</div> | |
</div> |
var shoppingListApp = angular.module('shoppingListApp', []); | |
shoppingListApp.controller('listController', function($scope) { | |
// Set the demo item array. | |
$scope.listItems = [ | |
{description: 'Your First Item', price: 2.49, coupons: [{name: 'Great Coupon', discount: .99}]}]; | |
// Set the state taxes array. | |
$scope.states = [ | |
{name: 'AL', tax: 4},{name: 'AK', tax: 0},{name: 'AZ', tax: 5.6},{name: 'AR', tax: 6.5},{name: 'CA', tax: 7.5}, | |
{name: 'CO', tax: 2.9},{name: 'CT', tax: 6.35},{name: 'DE', tax: 0},{name: 'DC', tax: 5.75},{name: 'FL', tax: 6},{name: 'GA', tax: 4},{name: 'HI', tax: 4},{name: 'ID', tax: 6},{name: 'IL', tax: 6.25},{name: 'IN', tax: 7},{name: 'IA', tax: 6},{name: 'KS', tax: 6.15},{name: 'KY', tax: 6},{name: 'LA', tax: 4},{name: 'ME', tax: 5.5},{name: 'MD', tax: 6},{name: 'MA', tax: 6.25},{name: 'MI', tax: 6},{name: 'MN', tax: 6.875},{name: 'MS', tax: 7},{name: 'MO', tax: 4.225},{name: 'MT', tax: 0},{name: 'NE', tax: 5.5},{name: 'NV', tax: 6.85},{name: 'NH', tax: 0},{name: 'NJ', tax: 7},{name: 'NM', tax: 5.125},{name: 'NY', tax: 4},{name: 'NC', tax: 4.75},{name: 'ND', tax: 5},{name: 'OH', tax: 5.75},{name: 'OK', tax: 4.5},{name: 'OR', tax: 0},{name: 'Other', tax: '99'},{name: 'PA', tax: 6},{name: 'RI', tax: 7},{name: 'SC', tax: 6},{name: 'SD', tax: 4},{name: 'TN', tax: 7},{name: 'TX', tax: 6.25},{name: 'UT', tax: 4.7},{name: 'VT', tax: 6},{name: 'VA', tax: 4.3},{name: 'WA', tax: 6.5},{name: 'WV', tax: 6},{name: 'WI', tax: 5},{name: 'WY', tax: 4}]; | |
// Set the default tax rate. 1 = total is multiplied by 1 (aka no tax) | |
$scope.taxRate = 1; | |
// Script to update the cost before tax | |
$scope.getBeforeTax = function () { | |
var total = 0; | |
for (t=0; t<$scope.listItems.length; t++) { | |
total = total + Number($scope.listItems[t].price); | |
} | |
$scope.totalBeforeTax = total.toFixed(2); | |
} | |
// Script to update the cost after tax | |
$scope.getAfterTax = function (rate) { | |
var totalAfterTax = Number($scope.totalBeforeTax) * Number(rate); | |
$scope.costAfterTax = totalAfterTax.toFixed(2); | |
} | |
// Script to remove an item from the shopping list | |
$scope.removeListItem = function (itemToRemove) { | |
var arrayIndex = this.listItems.indexOf(itemToRemove); | |
this.listItems.splice(arrayIndex, 1); | |
$scope.getBeforeTax(); | |
$scope.getAfterTax(this.taxRate); | |
$scope.updateCouponSavings(); | |
$scope.getFinalCost(); | |
}; | |
// Script to add an item to the shopping list | |
$scope.addListItem = function (descriptionToPush, price) { | |
if (typeof price == 'string') { | |
price = Number(price.replace(/[^\d\.]/g,'')); | |
price = price.toFixed(2); | |
} | |
this.listItems.push({description: descriptionToPush, price: price, coupons: []}); | |
this.newDescription = ""; | |
this.newPrice = ""; | |
$scope.getBeforeTax(); | |
$scope.getAfterTax(this.taxRate); | |
$scope.updateCouponSavings(); | |
$scope.getFinalCost(); | |
}; | |
// Script to see if tax selected is "Other" and show Other Tax option field. | |
$scope.toggleOtherTax = function (taxes) { | |
if (Number(taxes) == 99) { | |
$('#OtherTax').toggle(500); | |
this.taxRate = 1; | |
$scope.getAfterTax(this.taxRate); | |
} | |
if (Number(taxes) != 99 && $('#OtherTax').css('display') != 'none') { | |
$('#OtherTax').toggle(500); | |
} | |
if (Number(taxes) != 99) { | |
this.taxRate = 1 + (Number(taxes) / 100); | |
$scope.getAfterTax(this.taxRate); | |
} | |
$scope.getFinalCost(); | |
} | |
// Script to listen for keypresses in the other tax field and update accordingly the AfterTax and Final Cost fields. | |
$scope.addOtherTax = function () { | |
var newRate = Number($scope.otherTax.replace(/[^\d\.]/g,'')); | |
newRate = 1 + (newRate / 100); | |
$scope.getAfterTax(newRate); | |
$scope.getFinalCost(); | |
} | |
// Script to add a coupon. | |
$scope.addCoupon = function(thisItem, thisDesc, thisDisc) { | |
var arrayIndex = this.listItems.indexOf(thisItem); | |
var newDisc = Number(thisDisc.replace(/[^\d\.]/g,'')); | |
this.listItems[arrayIndex].coupons.push({name: thisDesc, discount: newDisc}); | |
$('.enterCoupon:eq('+arrayIndex+')').toggle(200); | |
$scope.updateCouponSavings(); | |
$scope.getFinalCost(); | |
} | |
// Script to show the coupon entry <ul><li> | |
$scope.maximizeCouponEnter = function(thisItem) { | |
var arrayIndex = this.listItems.indexOf(thisItem); | |
$('.enterCoupon:eq('+arrayIndex+')').toggle(200); | |
} | |
// Script to minimize the coupon entry <ul><li> | |
$scope.minimizeCouponEnter = function(thisItem) { | |
var arrayIndex = this.listItems.indexOf(thisItem); | |
$('.enterCoupon:eq('+arrayIndex+')').toggle(200); | |
} | |
// Script to remove a coupon. | |
$scope.removeCoupon = function(thisItem, thisCoupon) { | |
var arrayIndex = this.listItems.indexOf(thisItem); | |
var couponIndex = this.listItems[arrayIndex].coupons.indexOf(thisCoupon); | |
this.listItems[arrayIndex].coupons.splice(couponIndex, 1); | |
$scope.updateCouponSavings(); | |
$scope.getFinalCost(); | |
} | |
// Script to calculate coupon savings | |
$scope.updateCouponSavings = function() { | |
var totalSavings = 0; | |
for (t=0; t<$scope.listItems.length; t++) { | |
for (c=0; c<$scope.listItems[t].coupons.length; c++) { | |
totalSavings = totalSavings + Number($scope.listItems[t].coupons[c].discount); | |
} | |
} | |
$scope.couponSavings = totalSavings.toFixed(2); | |
$scope.getFinalCost(); | |
} | |
// Script to update the Final Cost | |
$scope.getFinalCost = function() { | |
var finalPrice = Number($scope.costAfterTax) - Number($scope.couponSavings); | |
finalPrice = finalPrice.toFixed(2); | |
$scope.finalPriceModel = finalPrice; | |
} | |
// Functions to run on module load. | |
$scope.getBeforeTax(); | |
$scope.getAfterTax(1); | |
$scope.updateCouponSavings(); | |
$scope.getFinalCost(); | |
}); |
@mainFonts: 'Quicksand', Arial, Helvetica, sans-serif; | |
// mixins | |
.centeredOnPage(@mtop) { | |
margin: @mtop auto; | |
} | |
.clearfix() { | |
zoom: 1; | |
&:before { content: ''; display: block; } | |
&:after { content: ''; display: table; clear: both; } | |
} | |
// styles | |
html, body { | |
padding: 0; | |
margin: 0; | |
} | |
a { | |
cursor: pointer; | |
} | |
h1 { | |
font-family: @mainFonts; | |
padding: 10px 0px 10px 0px; | |
font-size: 36px; | |
font-weight: 400; | |
color: #000; | |
} | |
h2 { | |
font-family: @mainFonts; | |
padding: 10px 0px 10px 0px; | |
font-size: 24px; | |
font-weight: 700; | |
color: #333; | |
border-bottom: 1px solid #ddd; | |
} | |
h3 { | |
font-family: @mainFonts; | |
font-size: 19px; | |
font-weight: 700; | |
color: #3A98D6; | |
padding: 0px 0px 10px 0px; | |
border-bottom: 1px solid #ddd; | |
} | |
p { | |
font-family: @mainFonts; | |
padding: 10px 0px 10px 0px; | |
font-weight: 300; | |
font-size: 20px; | |
color: #777; | |
} | |
.demoWrapper { | |
.clearfix(); | |
} | |
.box { | |
width: 800px; | |
.centeredOnPage(60px); | |
} | |
.shoppingList { | |
width: 480px; | |
margin: 0px 20px 0px 0px; | |
float: left; | |
font-family: @mainFonts; | |
ul { | |
list-style-type: none; | |
margin: 0; | |
padding: 0; | |
font-size: 19px; | |
color: #999; | |
} | |
li { | |
display: block; | |
padding: 0px; | |
margin: 0px 0px 7px 0px; | |
width: 100%; | |
.clearfix(); | |
} | |
.listItem { | |
float: left; | |
width: 47%; | |
} | |
.listPrice { | |
float: left; | |
width: 40%; | |
text-align: right; | |
} | |
.closeListItem { | |
float: right; | |
width: 10%; | |
text-align: right; | |
a { | |
color: #2376AD; | |
font-weight: 700; | |
text-decoration: none; | |
} | |
img { | |
display: inline-block; | |
margin-right: 6px; | |
} | |
} | |
} | |
.addItemModule { | |
width: 300px; | |
float: left; | |
margin: 0; | |
font-family: @mainFonts; | |
label { | |
font-size: 18px; | |
color: #999; | |
display: block; | |
padding: 5px 0px 5px 0px; | |
} | |
.addItemTBox { | |
border: 1px solid #ddd; | |
border-radius: 2px; | |
padding: 10px 0px 10px 5px; | |
width: 100%; | |
font-family: @mainFonts; | |
font-size: 18px; | |
font-weight: 700; | |
&:disabled { | |
background-color: #fff; | |
} | |
} | |
p { | |
font-size: 14px; | |
font-weight: 400; | |
color: #bbb; | |
} | |
a { | |
margin-top: 10px; | |
display: block; | |
padding: 10px; | |
text-align: center; | |
background-color: #2376AD; | |
border-radius: 3px; | |
border-bottom: 3px solid #155885; | |
text-decoration: none; | |
color: #fff; | |
font-weight: 700; | |
} | |
select { | |
border: 1px solid #ddd; | |
border-radius: 2px; | |
padding: 10px 0px 10px 5px; | |
width: 100%; | |
font-family: @mainFonts; | |
font-size: 18px; | |
font-weight: 700; | |
} | |
} | |
#OtherTax { | |
display: none; | |
} | |
.enterCoupon { | |
list-style-type: none; | |
display: none; | |
} | |
.enterCoupon li { | |
display: block; | |
input { | |
border: 1px solid #ddd; | |
border-radius: 3px; | |
padding: 4px 8px 4px 8px; | |
font-size: 16px; | |
font-family: @mainFonts; | |
font-weight: 700; | |
display: inline-block; | |
} | |
.newCouponName { | |
width: 45%; | |
} | |
.newCouponPrice { | |
width: 16%; | |
text-align: right; | |
} | |
.addCouponButton { | |
padding: 3px 10px 3px 10px; | |
text-align: center; | |
background-color: #2376AD; | |
border-radius: 3px; | |
border-bottom: 3px solid #155885; | |
text-decoration: none; | |
color: #fff; | |
font-size: 16px; | |
font-weight: 700; | |
} | |
.minimize { | |
padding: 3px 10px 3px 10px; | |
text-align: center; | |
background-color: #ddd; | |
border-radius: 3px; | |
border-bottom: 3px solid #bbb; | |
text-decoration: none; | |
color: #2376AD; | |
font-size: 16px; | |
font-weight: 700; | |
} | |
} | |
.couponsList { | |
list-style-type: none; | |
} | |
.couponsList .couponItem { | |
display: block; | |
margin: 0; | |
padding: 0; | |
width: 100%; | |
font-size: 15px; | |
font-weight: 700; | |
} | |
.couponsList li .thisCouponDescription { | |
display: inline-block; | |
width: 210px; | |
margin-left: 111px; | |
text-align: left; | |
} | |
.couponsList li .thisCouponDiscount { | |
display: inline-block; | |
width: 105px; | |
text-align: right; | |
} | |
.couponsList li a { | |
text-decoration: none; | |
display: inline-block; | |
width: 10px; | |
margin-left: 5px; | |
color: #2376AD; | |
} |