Build a payment crud application using AngularJS.

Problem. Develop an application with a simple layout with left navigation as a list of items and right side as item description upon the selection of any item. E.g.

 

Styling will match the above image with certain set of functionalities mentioned below:

  1. Initially detail form should be hidden
  2. Details form is dynamic – when the user clicks on the item the details form shows and is populated with the details of the payment.
  3. When the user clicks the cancel button , the details form is hidden again.
  4. Upon selection of the item form the list, highligh it in cyan color.
  5. When the user is viewing the details of a payment, they can modify all of the fields except for the ID.
  6. When the user clicks the save button the changes to the details are saved, and displayed in the list.
  7. Changes on the form are NOT reflected in the list until the user clicks the save button
  8. If the user makes some changes but clicks Cancel instead of Save, their modifications are discarded.
  9. When the user clicks the save button, the payment details form is hidden.
  10. If the user revisits the payment, they see the modifications that were previously made in the details form.

 

Solution: Let’s start building the application with the above use case.

Step 1 : Create a html page name it whatever you like but for current example I am naming it to index.html and add the mentioned below code.

 

<html ng-app="paymentsApp">

  <head>
    <link rel="stylesheet" href="style.css" />
  </head>

  <body ng-controller="PaymentsController as vm">
    <div class="application">
      <div class="title-bar">
        <h1 id="paymentsApp-title">Payments-o-matic</h1>
      </div>
      <div class="container">
        <div class="list">
          <div ng-repeat="payment in vm.payments" id="paymentsApp-list-{{$index}}">
           <div high-light item="{{payment}}">  
              <div>{{payment.counterparty}}</div>
              <div>{{payment.valueDate}}</div>
           </div>
          </div>
        </div>
        <div  ng-if="vm.isFormHidden">
          <button id="paymentsApp-btn-new" ng-click="vm.handleNewPayment()">Create new payment</button>
        </div>
        <form name="paymentForm">
        <div class="details" ng-if="!vm.isFormHidden" id="paymentsApp-detailsForm">
          <div>
            <div>
              <label>Counterparty</label>
              <input name="counterparty" ng-model="vm.selectedPayment.counterparty" id="paymentsApp-inpt-cpty" ng-maxlength="50" maxlength="50" ng-minlength="1" minlength="1" />
              <span ng-show="paymentForm.counterparty.$touched && paymentForm.counterparty.$invalid">Error on counterparty field.</span>
            </div>
            <div>
              <label>Value Date</label>
              <input  name="valueDate" ng-model="vm.selectedPayment.valueDate" id="paymentsApp-inpt-date" />
               <span ng-show="paymentForm.valueDate.$touched && paymentForm.valueDate.$invalid">Error on valueDate field.</span>
            </div>
            <div>
              <label>Credit Account</label>
              <input  name="creditAccount"  ng-model="vm.selectedPayment.creditAccount" id="paymentsApp-inpt-acc" ng-maxlength="8" maxlength="8" />
              <span ng-show="paymentForm.creditAccount.$touched && paymentForm.creditAccount.$invalid">Error on creditAccount field</span>
            </div>
            <div>
              <label>Amount</label>
              <input  name="amount"  ng-model="vm.selectedPayment.amount" id="paymentsApp-inpt-amt"  />
               <span ng-show="paymentForm.amount.$touched && paymentForm.amount.$invalid">Error on amount field.</span>
            </div>
            <div>
              <label>Currency</label>
              <input  name="currency" ng-model="vm.selectedPayment.currency" id="paymentsApp-inpt-cur" ng-maxlength="3" maxlength="3" />
               <span ng-show="paymentForm.currency.$touched && paymentForm.currency.$invalid">Error on currency field</span>
            </div>
            <div>
              <button  id="paymentsApp-btn-save" ng-disabled="paymentForm.$invalid" ng-click="vm.handleSave(paymentForm.$valid)">Save</button>
              <button  id="paymentsApp-btn-cancel" ng-click="vm.handleCancel()">Cancel</button>
              <button  id="paymentsApp-btn-delete" ng-if="!vm.newPayment" ng-click="vm.handleDelete()">Delete</button>
            </div>
          </div>
        </div>
        </form>
      </div>
    </div>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://code.angularjs.org/1.5.6/angular.min.js"></script>
    <script src="script.js"></script>
  </body>

</html>

 

Step 2: Create script.js file for handling the ui events.

//IIFE hepls to maintain clean global namespace
(function() {
  var PaymentsController = function PaymentsController($scope) {
    var vm = this;
    vm.isFormHidden = true;
    vm.selectedPayment = null;
    vm.called = false;
    vm.newPayment = false;
    // vm.paymentformError = false

    //used to remove the highlighted class
    var removeClass = function() {
      $('div.list').find('div.customHighLight').removeClass('customHighLight');
    }

    vm.handleCancel = function handleCancel() {
      vm.isFormHidden = true;
      vm.selectedPayment = null;
      removeClass();
    };
    vm.handleNewPayment = function handleNewPayment() {
      vm.isFormHidden = false;
      vm.newPayment = true;
    }
    vm.handleDelete = function handleDelete() {
      var status = confirm("are you sure");
      if (status) {
        vm.payments = vm.payments.filter(function(obj) {
          return obj.id !== vm.selectedPayment.id;
        });
        vm.isFormHidden = true;
      }
    };
    vm.handleSave = function handleSave(isFormValid) {
      if (isFormValid) {
        if (vm.newPayment) {
          var ids = vm.payments.map(function(obj) {
            return obj.id
          });
          var maxId = Math.max.apply(Math, ids)
          vm.selectedPayment.id = ++maxId;
          vm.payments.push(vm.selectedPayment);
        } else {
          var idx = vm.payments.findIndex(function(obj) {
            return obj.id == vm.selectedPayment.id
          })
          if (idx > -1) {
            //angular.copy is used to avoid instant change on the list
            vm.payments[idx] = angular.copy(vm.selectedPayment);
          }

        }


      }else{
          vm.formError = true;
        }
    };


    vm.payments = [{
      id: 1,
      counterparty: "Test User1",
      amount: "2,240.00",
      currency: "GBP",
      valueDate: "22/10/2015",
      creditAccount: "68794832"
    }, {
      id: 2,
      counterparty: "Test User2",
      amount: "1,500.00",
      currency: "GBP",
      valueDate: "22/10/2015",
      creditAccount: "30921782"
    }, {
      id: 3,
      counterparty: "Test User3",
      amount: "22,000.00",
      currency: "USD",
      valueDate: "31/10/2015",
      creditAccount: "44236712"
    }];
  };

  var HighLightDirective = function() {

    return {
      restrict: 'AE',
      scope: false,
      controller: 'PaymentsController',
      controllerAs: 'vm',
      link: function($scope, elem, attr) {

        elem.bind("click", function() {
          event.preventDefault();
          var selectedItem = JSON.parse(attr.item);
          elem.parent().parent().find('div.customHighLight').removeClass('customHighLight');
          elem.addClass('customHighLight');
          $scope.$apply(function() {
            $scope.$parent.vm.selectedPayment = selectedItem;
            $scope.$parent.vm.isFormHidden = false;
            $scope.$parent.vm.newPayment = false

          });
        });
      }
    }

  }

  angular.module("paymentsApp", [])
    .controller("PaymentsController", PaymentsController)
    .directive('highLight', HighLightDirective)

}())

The above script is very clear, it contains predefined data for displaying in the list and some event handling methods.

The important one the highlight directive from line 85 which is responsible for highligting the selected list and populating its data in the respective form fields.

Step 3: Create style.css file inside the same directory and paste the below code.

.application {
    background-color:green;
    bottom: 0;
    display: table;
    font-family:"Segoe UI Light", Georgia, Serif;
    font-size: 1rem;
    height: 100%;
    left: 0;
    min-width: 37.5rem;
    padding: 0;
    position: absolute;
    right: 0;
    top: 0;
    width: 100%;
}
.title-bar {
    background-color: #000000;
    color: white;
    display: table-row;
    font-weight: normal;
    height: 0;
    margin: 0;
}
.title-bar * {
    padding:1rem;
}
.container {
    background-color: white;
    display: table-row;
    height: 100%;
    width: 100%;
}
.list {
    border-right: solid 5px black;
    float: left;
    height: 100%;
    max-width: 25rem;
    min-width: 12.5rem;
    width: 30%;
}
.list > div > div {
    border-bottom: solid 1px black;
    font-weight: bold;
    height: 3rem;
    padding: 0.5rem;
}
.list > div > div > div {
    float:left;
    width: 50%;
}
.list > div > div > div:last-child {
    text-align: right;
}

.customHighLight{
  background-color:cyan;
}
.error{
  color:red;
}

The above code will style the html page as per the requirement.