Series: Building a Shopping Cart

Shopping Cart Part 2-Persistence with LocalStorage

Published on Sep 02, 2016

We continue building a Shopping Cart- this time adding persistence via LocalStorage.

In addition to adding persistence, we also add a 'Clear Cart' button, add a purchase total, and fix a bug.


Links

Code

$ ember install ember-local-storage
$ ember g storage cart -a
//services/cart.js
import Ember from 'ember';
import { storageFor } from 'ember-local-storage';

export default Ember.Service.extend({
  //...
  monsterIds: storageFor('cart'),
  monsters: Ember.computed('monsterIds.[]', function(){
    if(this.get('monsterIds.length') == 0){
      return []
    } else {
      return this.get('store').query('monster', {ids: this.get('monsterIds.content')})
    }
  }),
  monsterPrices: Ember.computed.mapBy('monsters', 'price'),
  total: Ember.computed.sum('monsterPrices'),
  clear(){
    this.get('monsterIds').clear()
  }
}
//templates/checkout.hbs
<div class="cart">
  {{#each cart.monsters as |monster|}}
    //... nothing changed in here
  {{/each}}
  <button {{action 'clear'}}>Clear Cart</button>
  <hr>
  Total: ${{cart.total}}
</div>
//controllers/checkout.js
import Ember from 'ember';

export default Ember.Controller.extend({
  cart: Ember.inject.service(),
  actions: {
    removeItem(monsterId){
      this.get('cart').remove(monsterId)
    },
    clear(){
      this.get('cart').clear()
    }
  }
});

Transcript

Welcome to Part 2 in our series on building a shopping cart in Ember. So today we’ll be building several new features. We’ll be having the total amount in the current shopping cart, we’ll be adding a ‘Clear’ button to completely remove everything from the shopping cart, we’ll fix this bug where when you remove everything it shows all the monsters, and we’ll also add in some local storage support so that when you reload the page it doesn’t reset your shopping cart.

First, we’ll tackle the total amount. So what we’ll do is we’ll just display the total amount and try to get it from the cart. That means in the cart we’ll have to have a computed property of total. Now what will this be computed on? So we’ve got our monsters and each of those has a price, so we’ll be wanting to sum up the prices of the monsters. So there is a computed macro called sum, and if we get something called monsterPrices, then that would be great. Luckily there is an easy way to get monsterPrices, and that is with another computed property, the mapBy property, so that takes the monsters and gets the price from them. So with these two computed properties, we’re able to get the total. So here, 18 plus 5 is 23. Awesome. And we’ll go ahead and add a dollar sign before that.

Now we’ll want a button that can clear the cart. So we’ll place the button within the checkout template, and then we’ll create the clear action which just sends it back up to the cart service. Then in our cart service, the clear function will just set the monsterIds attribute to an empty array. Let’s see this in action. We hit ‘Clear’ and oh, it says zero items in our cart, but lookie there, there’s our nice bug. So what’s happening here is when we’re calling query, if it sends up an empty array, then in our Rails app it will interpret this as false so it’ll just call Monster.all. Now we can change our server, or we can change how we do the monsters call. And since we don’t always have direct control over our server, let’s change it here. So here we’ll be doing an if statement, and so if the monsterIds.length is equal to zero, then we’ll just return an empty array. Otherwise, we’ll return the query like we were before.

So looking good so far. Let’s clear the cart. And there we go, we’re down to zero, just like we had hoped. Alright, now that we have our cart displaying as it should, let’s get around to saving the state of our cart, because if we reload this, then it’ll just go back to our default, and that’s no good. Users reload pages all the time and we don’t want them losing everything when they do that. To help us do this, we’re going to use the ember-local-storage addon, and what this will do is let us selectively chose things to put in the local storage. This is not an ember data adapter that will put all of our data in local storage. This will just put certain things like our shopping cart there.

So we’ll start off by installing ember-local-storage. Then we’ll generate a storage class, and we’ll call this storage class cart, and then we’ll do -a to recognize that it’s an array, so it will automatically generate it for an array instead of an object. Then when we go to storages/cart, then we’ll see that we’ve created a StorageArray based off the class that comes from ember-local-storage. Let’s use this in our cart service. First we’ll import the storageFor function, and we’ll import that from ember-local-storage. And then we’ll use that to replace the monsterIds. So we’ll use storageFor and then we’ll say cart here. So what we’re referencing as monsterIds is using this cart storage.

So it starts off with zero items in the cart because we removed the default two items. When we hit ‘Buy Plushy’, it seems to add an item. This is going great. Then when we click the shopping cart... Oh-oh, there is a problem. So let’s put a debugger in here to see what’s going on. So we’ll see that the monsterIds that we’re getting back... this is now a class and not an array, so if we try to get the length on this, that's a computed property. But if we get monsterIds.length, then that actually returns a length, so that’s the first thing we’ll change.

Then we’ll have to change how we get the content here because it’s no longer enough just to send it that class. We’ll have to call monsterIds.content, and that will get us the array we need. So we’ll get monsterIds.content and then we can delete this up here. So let’s see how this works. There we go. Here we have both of our items showing up, and of course to make sure ‘Clear Cart’ still works, and then adding more monsters... Oh-oh, this bug is showing up again. The reason it’s doing that is because we set the monsterIds just to an empty array, which is overriding the storageFor('cart') that we have. So let’s remake our clear function. First, we’ll get the monsterIds and then we’ll call .clear on them. That’s something that’s built in to the local storage addon. So let’s try this again. We’ll hit ‘Clear Cart’ and then we’ll see if we can add in more monsters. And it appears that it’s worked correctly this time.

So that’s part 2 of building a shopping cart. We covered local-storage, so that things will stay on when you reload, and we also created the total and the Clear Cart button, as well as fixed a bug related to that.

In the next video we'll be adding a way to accept payments using Stripe Checkout.

Building a Shopping Cart

Subscribe to our mailing list