In the last post, we created  the skeleton of our Ionic application and added some basic queries to Marketcloud using the marketcloud javascript library. In this post, we are going to add a cart to our app.


The first thing to do is to add a cart view that will let our customers add products to cart and perform a checkout.

The first step is to add a cart route in our app.js file :


  .state('app.cart', {
      url: '/cart',
      views: {
        'menuContent': {
          templateUrl: 'templates/cart.html',
          controller: 'CartCtrl'
        }
      }
  })

Let's create a new file called cart.html inside the www/templates directory and add the following content to it:

<ion-view view-title="Cart">
        <ion-content>
           <div ng-show="cart.items.length === 0">The cart is empty</div>
        </ion-content>
</ion-view>

This view will just show that the cart is empty,  in order to populate the cart view with real data we still have work to do.

Now let's create the controller for this view, open www/js/controllers.js and add the following controller:



.controller('CartCtrl',function($scope){
  $scope.cart = {
    items : []
  }
})

At this point, we have a new state in our app, but it's not reachable using the user interface! We have to add a shortcut to the shopping cart in the menu bar.

Let's open www/templates/menu.html and add the following html snippet right before the ion-nav-bar closing tag


<ion-nav-buttons side="right">
    <a class="button button-icon button-clear ion-android-cart" href="#/app/cart"></a>
</ion-nav-buttons>

This will add a shopping cart icon in every view, so that our cart is always reachable through the application.

To make your git repository jump to the current state, use the following command:

git checkout df74638beba3fcf5c939007dfd6d631fd1da9c6c


Now it's time to add the code to our cart. The first thing we do is to write a service to wrap all our HTTP calls to the Marketcloud backend.

This service will expose methods that allow us to create a cart and update its content, it will also return promises, so that we can just use these methods inside a resolve and angular will handle the rest (pun intended).




.service('CartService',function(marketcloud,$q){
  return {
    data : null,
    // Returns a cart by id
    getById : function(id) {
      var _this = this;
      return $q(function(resolve,reject){
        marketcloud.carts.getById(id,function(err,cart){
          if (err)
            reject(err)
          else{
            _this.data = cart;
            resolve(cart)
          }
        })
      })
    },
    //Creates a new cart 
    create : function(items) {
      var _this = this;

      return $q(function(resolve,reject){
        marketcloud.carts.create(items || [],function(err,cart){
          if (err)
            reject(err)
          else{
            _this.data = cart;
            resolve(cart)
          }
        })
      })
    },
    //Add items to cart
    add : function(items) {
      var _this = this;
      if (!this.data)
        throw new Error("Cart must be initialized first!")
      return $q(function(resolve,reject){
        marketcloud.carts.add(_this.data.id,items,function(err,cart){
          if (err)
            reject(err)
          else{
            _this.data = cart;
            resolve(cart)
          }
        })
      })
    },
    //Updates cart's contents
    update : function(update) {
      var _this = this;
      if (!this.data)
        throw new Error("Cart must be initialized first!")
      return $q(function(resolve,reject){
        marketcloud.carts.update(_this.data.id,update,function(err,cart){
          if (err)
            reject(err)
          else{
            _this.data = cart;
            resolve(cart)
          }
        })
      })
    },    
    //removes items from the cart
    remove : function(items) {
      var _this = this;
      if (!this.data)
        throw new Error("Cart must be initialized first!")
      return $q(function(resolve,reject){
        marketcloud.carts.remove(_this.data.id,items,function(err,cart){
          if (err)
            reject(err)
          else{
            _this.data = cart;
            resolve(cart)
          }
        })
      })
    },      
  }
})

Now we have to stop a second and think about how to handle the cart creation.

At the application's bootstrap we need to know whether the user already has a cart or not, and to persist a cart we will use the local storage.



.run(function(CartService){
  


  var promise = null;

  if (window.localStorage['marketcloud.cart_id']){
    promise = CartService.getById(window.localStorage['marketcloud.cart_id']);
  }
  else{
    promise = CartService.create([]);
  }
  
  promise
  .then(function(data){
    window.localStorage['marketcloud.cart_id'] = data.id;
  })
  .catch(function(err){
    console.log("Unable to init the cart")
  })

})

The piece of code above does a very simple thing: if the user already has a cart id in the local storage we use that id to retrieve the cart from the backend. Otherwise, a new cart is created.

Now it's time to let our customers add products to the cart. In order to do this we will add an "add to cart" button on the product view:

<button class="button button-balanced button-block" ng-click="addToCart()"><span class="ion ion-android-cart"></span> Add to cart</button>

As you can see, we are telling the framework to invoke the addToCart() method whenever the button is clicked, so we need to implement this method.
Open controllers.js and edit the controller named ProductCtrl:


.controller('ProductCtrl', function($scope, product, $ionicLoading, CartService, $ionicPopup) {
  $scope.product = product;

      $scope.addToCart = function() { 

      $ionicLoading.show({template : 'Adding to cart...'});

      CartService.add([{
          product_id: $scope.product.id,
          quantity: 1
        }])
        .then(function(cart){
          $ionicLoading.hide();
          $ionicPopup.alert({
              title: 'Message',
              template: 'The item was added to cart'
          });
        })
        .catch(function(error){
          $ionicLoading.hide();
          $ionicPopup.alert({
              title: "Error",
              template: "An error has occurred, please try again."
          })
        })
    }



})

Our application is now able to add products to the cart, but the cart view is still empty. We want it to show every product added to the cart with the right quantity. We also want it to let us increase and decrease the quantity of each item in the cart.

Update cart.html with this very basic structure:

<ion-view view-title="Cart">
  <ion-content>
<!-- if the cart has no elements -->
<div ng-show="cart.items.length === 0">
  <div>Your Cart is empty</div>
</div>

<!-- if the cart has some products -->

<div ng-hide="cart.items.length === 0">
  <div class="list">
    <div class="item item-thumbnail-left" ng-repeat="item in cart.items">
      <img ng-src="{{item.images[0]}}">
      <h2>{{ item.name }}</h2>
      <h4>$ {{ item.price }}</h4>
      <div>
        <a class="ion ion-plus"></a>
        {{item.quantity}}
        <a class="ion ion-minus"></a>
      </div>
    </div>
    <div class="item">
      <div class="padding">
        <h3>Total: $ {{getCartTotal()}}</h3>
      </div>
    </div>
  </div>
  <div class="padding">
    <a class="button button-positive button-block" href="#/app/checkout"><span class="ion ion-android-done-all"></span> Checkout</a>
  </div>
</div>
</ion-content>
</ion-view>

Our view is now able to render the list of products inside the cart, but the Cart controller has still no implementation able to retrieve the cart's data. As usual, we will retrieve data from Marketcloud during the resolve phase, updating the cart state:


  .state('app.cart', {
      url: '/cart',
      views: {
        'menuContent': {
          templateUrl: 'templates/cart.html',
          controller: 'CartCtrl'
        }
      },
      resolve:{
        cart : function(CartService) {
          return CartService.getById(window.localStorage['marketcloud.cart_id'])
        }
      }
  })

The controller must be updated too:


.controller('CartCtrl',function($scope,cart){
  $scope.cart = cart;
})

As you can see, the cart now shows every item, but we still have some work to do. 



You can jump to the current project state by using git:

git checkout e9d6212f79467c11dd8bc21b43e0cc1a28bb0045

In the next article, we will add controls required to manipulate the cart contents