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; | |
| } |