Fairway Technologies Blog

blog

Build a Credit Card Entry Page using Angular – Part 1

In Development No Comments

A common page on many public websites is a page that asks a user to submit their credit card information. This seemingly simple little page has quite a few moving pieces in it. This series of blog posts illustrates how to build the HTML, Web API calls, a view model class, the Entity Framework objects, and the appropriate AngularJS controller to create a credit card entry page. Yes, I am still using AngularJS (or Angular 1) as opposed to Angular 2. The reason for this is I am finding that many developers are more familiar with JavaScript than with TypeScript and wish to stay with a language they know. There is nothing wrong with Angular 1, and thus no compelling reason to upgrade to Angular 2 if you don't want to.

In the first part of this blog post series you build the basic HTML for the credit card entry page. You also load the credit card types, months and years into drop-down lists on the page using hard-coded data. Succeeding blog posts will show how to build a Web API, Entity Framework classes, and a view model to support getting and storing credit card data from a set of SQL Server tables.

Overview of SPA Architecture

In this first blog post, you are going to layout the overall SPA pages needed to support the credit card data entry page. Figure 1 shows the two pages you are going to create for this system. The index.html page is like a Master Page in Web Forms or a Shared Layout page in MVC in that it contains the "chrome" for all the pages you route to. The only page in this post you are going to route to is the creditcard.html page, but the ng-view directive can be used for as many pages as you need in your application.

CreditCard-Part1-Figure1.jpg

Figure 1: Overall architecture of SPA architecture

The creditcard.html page (shown in Figure 2) is where the user enters their credit card information. The index-splash.html page is displayed when the user first enters the system. If you have a ng-view directive, you must always have something displayed in it. If not, Angular will go into a recursive loop and eventually error out.

CreditCard-Part1-Figure2.jpgFigure 2: The Credit Card data entry page

Build the Main SPA Page

You are going to build a Single Page Application (SPA) to illustrate how to build the credit card page. This means you build a single index.html page from which you call the credit card page in your application. This sample will just contain the one credit card HTML page, but it is good to build your application using the SPA design pattern so you can add additional pages later.

Open Visual Studio 2015 and select File | New | Project. Choose Web under the Visual C# templates, then select ASP.NET Web Application (.NET Framework). Set the Name of the project to CreditCardEntry and click the OK button. Select the Empty template, and check the Web API check box before pressing the OK button as shown in Figure 3.

CreditCard-Part1-Figure3.jpg

Figure 3: Select an Empty project template using the Web API

Using the NuGet Package Manager tool, install the packages needed for the complete sample. You are not going to use the Entity Framework yet, but in the next blog posts you will need this, so you might as well add it now.

  • EntityFramework
  • AngularJS.Core
  • AngularJS.Route
  • Bootstrap

Add a new HTML page in the root of your project called index.html. Modify the page to look like Figure 4 using the HTML shown in the listing below:

<pre>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&nbsp; &nbsp; &lt;title&gt;Credit Card Entry Sample&lt;/title&gt;
&nbsp; &nbsp; &lt;meta charset="utf-8" /&gt;
&nbsp; &nbsp; &lt;link href="Content/bootstrap.min.css"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rel="stylesheet" /&gt;
&lt;/head&gt;
&lt;body&gt;
&nbsp; &nbsp; &lt;div class="container"
&nbsp; &nbsp; &nbsp; &nbsp; <strong>ng-app="app"</strong>
&nbsp; &nbsp; &nbsp; &nbsp; <strong>ng-controller="IndexController as vm"&gt;</strong>
&nbsp; &nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &lt;div class="col-sm-10"&gt;
&nbsp; &nbsp; &lt;h1&gt;Credit Card Entry System in Angular
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/h1&gt;
&nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &lt;div class="col-sm-10"&gt;
&nbsp; &nbsp; &lt;a href="#/creditcard"
&nbsp; &nbsp; &nbsp; class="btn btn-primary"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Credit Card Entry
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/a&gt;
&nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;!-- Separator Line --&gt;
&nbsp; &nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &lt;div class="col-sm-10"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;!-- ** BEGIN PARTIAL VIEWS AREA --&gt;
&nbsp; &nbsp; <strong>&lt;div ng-view&gt;&lt;/div&gt;</strong>
&nbsp; &nbsp; &lt;!-- ** END PARTIAL VIEWS AREA --&gt;
&nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;script src="scripts/angular.js"&gt;&lt;/script&gt;
&nbsp; &nbsp; &lt;script src="scripts/angular-route.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>

Towards the bottom of the page you see a <div> tag with an attribute of ng-view. This attribute tells Angular where you wish to insert partial pages within this page. The # sign as the first character in the <a> tag, followed by the /creditcard informs Angular you want to find an Angular route named "creditcard" and to load that partial page within the <div> tag with the ng-view attribute.

CreditCard-Part1-Figure4.jpg

Figure 4: The main HTML page for your SPA

Modify the Global.asax.cs file to handle self-referencing in the Entity Framework and convert all pascal case property names to camel case. There is no self-referencing in this sample, but I find in many projects, this helps avoid some hard-to-track-down bugs.

<pre>protected void Application_Start() {
&nbsp; GlobalConfiguration.Configure(WebApiConfig.Register);
&nbsp; <strong>// Handle self-referencing in Entity Framework
&nbsp; HttpConfiguration config =
&nbsp; &nbsp; GlobalConfiguration.Configuration;
&nbsp; config.Formatters.JsonFormatter
&nbsp; &nbsp; .SerializerSettings.ReferenceLoopHandling =
&nbsp; &nbsp; &nbsp; Newtonsoft.Json.ReferenceLoopHandling.Ignore;
&nbsp; // Convert to camelCase
&nbsp; var jsonFormatter = config.Formatters
&nbsp; &nbsp; .OfType&lt;JsonMediaTypeFormatter&gt;().FirstOrDefault();
&nbsp; jsonFormatter.SerializerSettings.ContractResolver =
&nbsp; &nbsp; new CamelCasePropertyNamesContractResolver();</strong>
}
</pre>

Build Angular Folder and JavaScript Files

A popular style for laying out Angular applications is to create a \app folder in your project and place all your HTML and JavaScript files in this folder. Group your files into folders with the name of the page you are working on. There are two Angular-controlled pages in this sample; the index page and the credit card page. Create a folder structure like the one shown in Figure 5.

CreditCard-Part1-Figure5.png

Figure 5: Create your folder structure

Build Index Page JavaScript Files

In the \app\index folder add a new JavaScript file named index.module.js. This is where you create your Angular module that matches the name in the ng-app attribute of the <div> tag in your index.html page. As you are going to be using Angular routing include the 'ngRoute' module dependency.

<pre>(function () {
&nbsp; 'use strict';
&nbsp; angular.module('app', ['ngRoute']);
})();
</pre>

Create another JavaScript file named index.controller.js within the \app\index folder. In this file is where you define the Angular controller for this page. This page has no functionality other than to redirect to other pages, thus there is no code in this controller function.

<pre>(function () {
&nbsp; 'use strict';
&nbsp; angular.module('app').controller('IndexController',
&nbsp; &nbsp; IndexController);
&nbsp; function IndexController() {
&nbsp; }
})();
</pre>

Add one more JavaScript file to the \app\index folder named index.route.js. This file defines the various routes that you use in anchor tags on your index.html page.

<pre>(function () {
&nbsp; 'use strict';
&nbsp; // Create angular routing
&nbsp; angular.module('app')
&nbsp; &nbsp; .config(['$routeProvider', function ($routeProvider) {
&nbsp; &nbsp; &nbsp; $routeProvider
&nbsp; &nbsp; &nbsp; .when('/',
&nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; templateUrl: 'app/index/index-splash.html',
&nbsp; &nbsp; &nbsp; &nbsp; controllerAs: 'vm',
&nbsp; &nbsp; &nbsp; &nbsp; controller: 'IndexController'
&nbsp; &nbsp; &nbsp; })
&nbsp; &nbsp; &nbsp; .when('/creditcard',
&nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; templateUrl: 'app/creditcard/creditcard.html',
&nbsp; &nbsp; &nbsp; &nbsp; controllerAs: 'vm',
&nbsp; &nbsp; &nbsp; &nbsp; controller: 'CreditCardController'
&nbsp; &nbsp; &nbsp; })
&nbsp; &nbsp; &nbsp; .otherwise(
&nbsp; &nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; redirectTo: '/'
&nbsp; &nbsp; &nbsp; });
&nbsp; &nbsp; }]);
})();
</pre>

Each route is defined using the when() function. You pass to the when() function the route defined in an anchor tag, or to call using Angular location services. You also pass in an object that defines either some inline HTML or an HTML template to display within the ng-view area. You may optionally define the controller name and a 'controllerAs' property to name each controller reference.

The default route, defined as when('/'), tells Angular to redirect back to index.html. You must define either some HTML, or an HTML template to display within the ng-view area when someone requests a route or Angular gets caught in a loop which eventually causes an error. Personally, I like to define a little "splash" page to display some opening remarks to the user. Create a new HTML page in the \app\index folder named index-splash.html. Strip all HTML from the page that is added, and just add this one line to the file.

<pre>&lt;p&gt;This sample illustrates a credit card entry system.&lt;/p&gt;
</pre>

Open the index.html page and add the following <script> tags below the angular script tags.

<pre>&nbsp; &lt;script src="scripts/angular.js"&gt;&lt;/script&gt;
&nbsp; &lt;script src="scripts/angular-route.js"&gt;&lt;/script&gt;

<strong>&nbsp; &lt;script src="app/index/index.module.js"&gt;&lt;/script&gt;
&nbsp; &lt;script src="app/index/index.controller.js"&gt;&lt;/script&gt;
&nbsp; &lt;script src="app/index/index.route.js"&gt;&lt;/script&gt;</strong>
&lt;/body&gt;
&lt;/html&gt;
</pre>

You should be able to run the main index page. Don't click on the button as it is not hooked up yet.

Build the Credit Card HTML

Add an HTML page to the \app\creditcard folder named creditcard.html. This is a partial page, so we don't need any of the normal HTML tags, so go ahead and delete all HTML in this page. You only need to write the HTML to be loaded within the <div> tag with the ng-view attribute. The code shown below is not the final code, but is just the initial layout for the credit card HTML page.

<pre>&lt;div class="row"&gt;
&nbsp; &lt;div class="col-sm-8"&gt;
&nbsp; &nbsp; &lt;form name="creditCardForm" novalidate&gt;
&nbsp; &nbsp; &nbsp; &lt;div class="panel panel-default"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;div class="panel-heading"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;h3 class="panel-title"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Credit Card Information
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/h3&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;div class="panel-body"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;div class="panel-footer"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div class="col-xs-12"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;div class="pull-right"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;button type="button"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class="btn btn-primary"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ng-click=
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "vm.saveClick(creditCardForm)"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;i class="glyphicon
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; glyphicon-floppy-disk"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/i&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;nbsp;Save
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/button&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;a class="btn btn-primary"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; formnovalidate="formnovalidate"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; href="#/"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;i class="glyphicon
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; glyphicon-remove-circle"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/i&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;nbsp;Cancel
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/a&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;/form&gt;
&nbsp; &lt;/div&gt;
&lt;/div&gt;
</pre>

The credit card page is created within a Bootstrap row and a column. Add a <form> tag to encapsulate the input fields for the credit card data. Next, build the structure of the Bootstrap panel control into which all the messages and input fields will reside. The messages and input fields will be placed into the panel body. In the footer of the panel control you define a save and cancel button.

Add Credit Card Controller

For each partial web page, you need an Angular controller that goes with it. Add a JavaScript file in the \app\creditcard folder named creditcard.controller.js. Add the following code to this script file.

<pre>(function () {
&nbsp; "use strict";
&nbsp; angular.module("app")
&nbsp; &nbsp; .controller("CreditCardController", CreditCardController);
&nbsp; function CreditCardController($http, $location) {
&nbsp; &nbsp; var vm = this;
&nbsp; &nbsp; var dataService = $http;
&nbsp; &nbsp; // Create UI state object
&nbsp; &nbsp; vm.uiState = {
&nbsp; &nbsp; &nbsp; isMessageAreaHidden: true,
&nbsp; &nbsp; &nbsp; isLoading: true,
&nbsp; &nbsp; &nbsp; messages: []
&nbsp; &nbsp; };
&nbsp; }
})();
</pre>

In the above function the $http and $location services are injected into our controller. A common practice is to assign each of the services to a local variable. Assign the $scope from Angular to the variable vm, and the $http service to a variable named dataService.

The object in the credit card controller name uiState is used to hold properties that affect the user interface in some manner. The isMessageAreaHidden property turns on and off a Bootstrap alert area used to display messages to the user. The isLoading property determines whether to display a "Please wait while loading" message to the user when they first enter the web page or not. The messages array holds a collection of message objects that are displayed in the message area on the screen.

Error Message Area

Within the body of the panel control create a <div> tag to display error and validation messages to the user. This functionality for displaying messages will be added in a future post. Messages you add to the messages array you create in the controller are displayed within an unordered list. You can see the ng-repeat attribute is used to loop through each message in the array and display any values set in a message property within an <li> element. Within the <div class="panel-body"> add the HTML shown below.

<pre>&lt;!-- ** BEGIN MESSAGE AREA ** --&gt;
&lt;div ng-hide="vm.uiState.isMessageAreaHidden ||
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (creditCardForm.$valid &amp;&amp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; !creditCardForm.$pristine)"
&nbsp; &nbsp; class="row"&gt;
&nbsp; &lt;div class="col-xs-12"&gt;
&nbsp; &nbsp; &lt;div class="alert alert-danger
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alert-dismissable"
&nbsp; &nbsp; &nbsp; &nbsp; role="alert"&gt;
&nbsp; &nbsp; &nbsp; &lt;button type="button" class="close"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data-dismiss="alert"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;span aria-hidden="true"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;times;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/span&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;span class="sr-only"&gt;Close&lt;/span&gt;
&nbsp; &nbsp; &nbsp; &lt;/button&gt;
&nbsp; &nbsp; &nbsp; &lt;ul&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;li ng-repeat="msg in vm.uiState.messages"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/li&gt;
&nbsp; &nbsp; &nbsp; &lt;/ul&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &lt;/div&gt;
&lt;/div&gt;
&lt;!-- ** END MESSAGE AREA ** --&gt;
</pre>

The <div> within the panel body uses the Angular ng-hide attribute to hide this area based on a few different flags. The isMessageAreaHidden property in the vm.uiState object in your controller is set by you depending on whether or not there are validation messages to be displayed. You check two other properties on the creditCardForm in addition to the isMessageAreaHidden property. If the creditCardForm is valid is true and pristine property is false, then the message area will be hidden.

Loading Message Area

Add another <div> tag just after the message area you created. This <div> tag uses the Angular ng-show attribute so this area is only displayed when a property in your controller, vm.uiState.isLoading, is set to a true value. When this page is first loaded, all the drop-down lists need to be loaded. Calling the Web API to get this data can take a couple of seconds on the first load, so it is a good idea to display a "Please wait while loading" message to the user before you display any other user interface items to them.

<pre>&lt;!-- ** BEGIN LOADING MESSAGE AREA ** --&gt;
&lt;div class="row" ng-show="vm.uiState.isLoading"&gt;
&nbsp; &lt;div class="col-sm-offset-1 col-sm-10
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alert alert-warning"&gt;
&nbsp; &nbsp; &lt;div class="text-center"&gt;
&nbsp; &nbsp; &nbsp; Please wait while loading
&nbsp; &nbsp; &nbsp; &nbsp; credit card information...
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &lt;/div&gt;
&lt;/div&gt;
&lt;!-- ** END LOADING MESSAGE AREA ** --&gt;
</pre>

Input Fields

After the loading message area above, add another <div> element which is hidden until the vm.uiState.isLoading property is set to a false value. In the controller, this property is initially set to a true value. After all the drop-down lists have been loaded, this property is set to false. At that time the loading message will be hidden and the user input fields within this <div> tag will be displayed. Below is the HTML for each of the input fields required for the credit card data. You have not added the Angular binding yet, but will do that later in this post.

<pre>&lt;!-- ** BEGIN CREDIT CARD ENTRY AREA ** --&gt;
&lt;div ng-hide="vm.uiState.isLoading"&gt;
&nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &lt;div class="form-group col-sm-6"&gt;
&nbsp; &nbsp; &nbsp; &lt;label for="types"&gt;Select Credit Card Type
&nbsp; &nbsp; &nbsp; &lt;/label&gt;
&nbsp; &nbsp; &nbsp; &lt;select id="types"
&nbsp; &nbsp; &nbsp; &nbsp; name="types"
&nbsp; &nbsp; &nbsp; &nbsp; class="form-control"&gt;
&nbsp; &nbsp; &nbsp; &lt;/select&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &lt;/div&gt;
&nbsp; &lt;div class="form-group"&gt;
&nbsp; &nbsp; &lt;label for="nameOnCard"&gt;Name on Card&lt;/label&gt;
&nbsp; &nbsp; &lt;input id="nameOnCard"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name="nameOnCard"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; placeholder="Name on Card"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title="Name on Card"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type="text" /&gt;
&nbsp; &lt;/div&gt;
&nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &lt;div class="form-group col-sm-8"&gt;
&nbsp; &nbsp; &nbsp; &lt;label for="cardNumber"&gt;Credit Card Number
&nbsp; &nbsp; &nbsp; &lt;/label&gt;
&nbsp; &nbsp; &nbsp; &lt;input id="cardNumber"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name="cardNumber"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; placeholder="Credit Card Number"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title="Credit Card Number"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type="text" /&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;div class="form-group col-sm-4"&gt;
&nbsp; &nbsp; &nbsp; &lt;label for="securityCode"&gt;Security Code
&nbsp; &nbsp; &nbsp; &lt;/label&gt;
&nbsp; &nbsp; &nbsp; &lt;input id="securityCode"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name="securityCode"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; placeholder="Security Code"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title="Security Code"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type="text" /&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &lt;/div&gt;
&nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &lt;div class="form-group col-sm-8"&gt;
&nbsp; &nbsp; &nbsp; &lt;label for="expMonths"&gt;Exp. Month&lt;/label&gt;
&nbsp; &nbsp; &nbsp; &lt;select id="expMonths"
&nbsp; &nbsp; &nbsp; &nbsp; name="expMonths"
&nbsp; &nbsp; &nbsp; &nbsp; class="form-control"&gt;
&nbsp; &nbsp; &nbsp; &lt;/select&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &nbsp; &lt;div class="form-group col-sm-4"&gt;
&nbsp; &nbsp; &nbsp; &lt;label for="expYears"&gt;Exp. Year&lt;/label&gt;
&nbsp; &nbsp; &nbsp; &lt;select id="expYears"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name="expYears"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class="form-control"&gt;
&nbsp; &nbsp; &nbsp; &lt;/select&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &lt;/div&gt;
&nbsp; &lt;div class="row"&gt;
&nbsp; &nbsp; &lt;div class="form-group col-sm-6"&gt;
&nbsp; &nbsp; &nbsp; &lt;label for="billingPostalCode"&gt;
&nbsp; &nbsp; &nbsp; &nbsp; Billing Postal Code
&nbsp; &nbsp; &nbsp; &lt;/label&gt;
&nbsp; &nbsp; &nbsp; &lt;input id="billingPostalCode"
&nbsp; &nbsp; &nbsp; &nbsp; name="billingPostalCode"
&nbsp; &nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; &nbsp; placeholder="Billing Postal Code"
&nbsp; &nbsp; &nbsp; &nbsp; title="Billing Postal Code"
&nbsp; &nbsp; &nbsp; &nbsp; type="text" /&gt;
&nbsp; &nbsp; &lt;/div&gt;
&nbsp; &lt;/div&gt;
&lt;/div&gt;
&lt;!-- ** END CREDIT CARD ENTRY AREA ** --&gt;
</pre>

Open the index.html page and add the following <script> tag below the other script tags you added earlier.

<pre>&nbsp; &lt;script src="scripts/angular.js"&gt;&lt;/script&gt;
&nbsp; &lt;script src="scripts/angular-route.js"&gt;&lt;/script&gt;
&nbsp; &lt;script src="app/index/index.module.js"&gt;&lt;/script&gt;
&nbsp; &lt;script src="app/index/index.controller.js"&gt;&lt;/script&gt;
&nbsp; &lt;script src="app/index/index.route.js"&gt;&lt;/script&gt;

<strong>&nbsp; &lt;script src="app/creditcard/creditcard.controller.js"&gt;
&nbsp; &lt;/script&gt;</strong>
&lt;/body&gt;
&lt;/html&gt;
</pre>

You should be able to run the main index page and click on the Credit Card Entry button to display your credit card page. The page will show the "Loading" message because the isLoading property is set to true. To see the input fields, go into the creditcard.controller.js file, change the isLoading property to false, and rerun the page. If you do this, be sure to set it back to true once you stop the web application.

Credit Card Controller

Let's add more of the properties to the CreditCardController function that you are going to require for the credit card page. You need array properties to load the drop-down lists. You need objects for selecting a credit card type and a month, and you need an object to hold properties for each input field. In addition, you need properties to turn on and off the various message areas on the page. Add the additional code shown below to your creditcard.controller.js file.

<pre>function CreditCardController($http, $location) {
&nbsp; var vm = this;
&nbsp; var dataService = $http;
&nbsp; // Expose public properties
&nbsp; vm.cardTypes = [];
&nbsp; vm.months = [];
&nbsp; vm.years = [];
&nbsp; vm.selectedCardType = {};
&nbsp; vm.selectedMonth = {};
&nbsp; vm.creditCard = {
&nbsp; &nbsp; creditCardId: null,
&nbsp; &nbsp; cardType: null,
&nbsp; &nbsp; nameOnCard: null,
&nbsp; &nbsp; cardNumber: null,
&nbsp; &nbsp; securityCode: null,
&nbsp; &nbsp; expMonth: null,
&nbsp; &nbsp; expYear: null,
&nbsp; &nbsp; billingPostalCode: null
&nbsp; };
&nbsp; vm.uiState = {
&nbsp; &nbsp; isMessageAreaHidden: true,
&nbsp; &nbsp; isLoading: true,
&nbsp; &nbsp; messages: []
&nbsp; };
&nbsp; // Initialize Controller
&nbsp; loadCardTypes();
&nbsp; loadYears();
&nbsp; loadMonths();
&nbsp; // Load Credit Card Types
&nbsp; function loadCardTypes() {

&nbsp; }
&nbsp; // Load years
&nbsp; function loadYears() {
&nbsp; &nbsp;
&nbsp; }
&nbsp; // Load months
&nbsp; function loadMonths() {
&nbsp; &nbsp;
&nbsp; }
}
</pre>

The three arrays in the scope for this controller hold the data to go into the three drop-down lists on the credit card page. The cardTypes array and month array are both object arrays. The years array is an array of integer values representing a year the user can select from.

<pre>vm.cardTypes = [];
vm.months = [];
vm.years = [];
</pre>

When the user selects a value from a drop-down that is bound to an object, you can bind to another object in your controllers' scope. Create two additional properties to bind to for the month and card type.

<pre>vm.selectedMonth = {};
vm.selectedCardType = {};
</pre>

The next object you create is one that holds the data for each input element on the screen. This object is named creditCard and is added to the scope just like the others. Within the creditCard object define a property to map to each input element.

<pre>vm.creditCard = {
&nbsp; creditCardId: null,
&nbsp; cardType: null,
&nbsp; nameOnCard: null,
&nbsp; cardNumber: null,
&nbsp; securityCode: null,
&nbsp; expMonth: null,
&nbsp; expYear: null,
&nbsp; billingPostalCode: null
};
</pre>

Load Mock Data for Drop-Down Lists

Looking at Figure 2 you know you need to load the three drop-down lists for credit card types, months and years. Let's finish building the routines you added to the creditCard controller to load the data into the properties of our controller. For this first sample you are going to hard-code the various values. In a later post you learn to connect to a back-end through a Web API to retrieve credit card type data from a SQL Server database table, and to load the months and years.

Load Credit Card Types

Open up the \app\creditcard\creditcard.controller.js file and locate the loadCardTypes() function. Modify the code to look like the code shown below:

<pre>function loadCardTypes() {
&nbsp; vm.cardTypes.push({ cardType: 'Visa' });
&nbsp; vm.cardTypes.push({ cardType: 'MasterCard' });
&nbsp; vm.cardTypes.push({ cardType: 'American Express' });
&nbsp; vm.cardTypes.push({ cardType: 'Discover' });
&nbsp; vm.selectedCardType = vm.cardTypes[0];
}
</pre>

In the above code push a new object with a single property onto the cardTypes array. The property is called cardType and you specify a value to display in the drop-down list on the HTML page. Feel free to add as many different card types as you wish to this list. Lastly, take the first cardType object and assign it to the selectedCardType property in your scope.

When you build the <select> HTML element you are going to use two Angular attributes; ng-modeland ng-options. The ng-model attribute binds to the vm.selectedCardType property in the CreditCardController. The ng-options attribute specifies how to load the <select> element. Go to the creditcard.html page, locate the types <select> element and add these two attributes.

<pre>&lt;select id="types"
&nbsp; &nbsp; &nbsp; &nbsp; name="types"
&nbsp; &nbsp; &nbsp; &nbsp; class="form-control"
<strong>&nbsp; &nbsp; &nbsp; &nbsp; ng-model="vm.selectedCardType"
&nbsp; &nbsp; &nbsp; &nbsp; ng-options="item.cardType for item in vm.cardTypes
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; track by item.cardType"&gt;</strong>
&lt;/select&gt;
</pre>

You can think of the ng-options attribute like a foreach loop in C#. Here would be the equivalent pseudo-code in C# for what the ng-options attribute is doing.

<pre>foreach (item in vm.cardTypes) {
&nbsp; &lt;option value=”item.cardType”&gt;item.cardType&lt;/option&gt;
}
</pre>

Let's break down each piece of the ng-options attribute value. The "for item" defines a local variable with the name of "item". The "in vm.cardTypes" specifies the property in the scope of your controller to retrieve the collection of data from. The "item.cardType" before the "for item" is the name of the property within each object in the cardType array you wish to display in the text portion of the drop-down. The "track by item.cardType" is the property from which to retrieve the value to put into the value portion of each <option> element within the <select>.

Load Years

I am seeing more and more credit card forms asking for a year up to 20 years in the future. So, for this sample, you load 20 years into the vm.years array. This array is simply an array of integer values. No object is necessary for each element in this array.

<pre>function loadYears() {
&nbsp; var year = new Date().getFullYear();
&nbsp; for (var i = 0; i &lt; 20 ; i++) {
&nbsp; &nbsp; vm.years.push((year + i));
&nbsp; }
&nbsp; vm.creditCard.expYear = year;
}
</pre>

Go to the creditcard.html page, locate the expYears <select> element and add these two attributes.

<pre>&lt;select id="expYears"
&nbsp; &nbsp; &nbsp; &nbsp; name="expYears"
&nbsp; &nbsp; &nbsp; &nbsp; class="form-control"
<strong>&nbsp; &nbsp; &nbsp; &nbsp; ng-model="vm.creditCard.expYear"
&nbsp; &nbsp; &nbsp; &nbsp; ng-options="item for item in vm.years track by item"&gt;</strong>
&lt;/select&gt;
</pre>

The ng-model attribute binds directly to the vm.creditCard.expYear property in the CreditCardController. Since each item in the years array is just an integer, that integer value is assigned to the expYear property.

The ng-options attribute uses just the variable name 'item' to bind to the text portion of the drop-down and to the value portion. Since each element is just an integer value, you do not specify any property name within 'item', you just use 'item' itself.

Load Months

The loadMonths() function is similar to the loadCardTypes() function in that you are creating an object to load into the vm.months array. The month object contains two properties; monthNumber and monthName. The monthNumber property will be used in the value portion of the drop-down list, while the monthName property will be used in the text portion of the drop-down.

After loading the objects, set the expYear and expMonth of the vm.creditCard object to the next month, or January if the current month is 12, and to either the current year, or the next year if the current month is 12. Below is the code to load the months and set the expYear and expMonth.

<pre>function loadMonths() {
&nbsp; var today = new Date();
&nbsp; vm.months.push({ monthNumber: 1, monthName: 'January' });
&nbsp; vm.months.push({ monthNumber: 2, monthName: 'February' });
&nbsp; vm.months.push({ monthNumber: 3, monthName: 'March' });
&nbsp; vm.months.push({ monthNumber: 4, monthName: 'April' });
&nbsp; vm.months.push({ monthNumber: 5, monthName: 'May' });
&nbsp; vm.months.push({ monthNumber: 6, monthName: 'June' });
&nbsp; vm.months.push({ monthNumber: 7, monthName: 'July' });
&nbsp; vm.months.push({ monthNumber: 8, monthName: 'August' });
&nbsp; vm.months.push({ monthNumber: 9, monthName: 'September' });
&nbsp; vm.months.push({ monthNumber: 10, monthName: 'October' });
&nbsp; vm.months.push({ monthNumber: 11, monthName: 'November' });
&nbsp; vm.months.push({ monthNumber: 12, monthName: 'December' });
&nbsp; // Figure out which month to select
&nbsp; // Make it next month by default
&nbsp; vm.creditCard.expMonth = today.getMonth() + 2;
&nbsp; // If past December, then make it January of the next year
&nbsp; if (vm.creditCard.expMonth &gt; 12) {
&nbsp; &nbsp; vm.creditCard.expMonth = 1;
&nbsp; &nbsp; vm.creditCard.expYear = vm.creditCard.expYear + 1;
&nbsp; }
&nbsp; vm.selectedMonth = vm.months[vm.creditCard.expMonth - 1];
&nbsp; // Set the page UI flag as not loading anymore
&nbsp; vm.uiState.isLoading = false;
}
</pre>

Go to the creditcard.html page, locate the expMonths <select> element and add these two attributes.

<pre>&lt;select id="expMonths"
&nbsp; &nbsp; &nbsp; &nbsp; name="expMonths"
&nbsp; &nbsp; &nbsp; &nbsp; class="form-control"
<strong>&nbsp; &nbsp; &nbsp; &nbsp; ng-model="vm.selectedMonth"
&nbsp; &nbsp; &nbsp; &nbsp; ng-options="item.monthName for item
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; in vm.months
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; track by item.monthNumber"&gt;</strong>
&lt;/select&gt;
</pre>

Just like for the card types, this <select> element is binding to the selectedMonth object in your controller.

The last thing the loadMonths function does is to set the isLoading property of the vm.uiState object to false. If you were calling the Web API in each of the above functions, these calls might take a little time. In the HTML page display a "Please wait while loading…" message to the user until the isLoading property is set to false. This is where that property is set to false, which makes the message disappear.

Bind Credit Card Input Fields

With your controller built and a creditCard object with properties to bind to the input fields, the only thing left to do is to perform the binding. Add a hidden input field directly below the <form> tag in your HTML. This hidden field is for the primary key value for the credit card record you add to a SQL Server database table. Initially it will be null, but after you add a record this value will be filled in and passed back from the Web API.

<pre>&lt;input type="hidden"
&nbsp; &nbsp; &nbsp; ng-model="vm.creditCard.creditCardId" /&gt;
</pre>

Locate the nameOnCard input field and bind to the creditCard object by adding the following ng-model attribute.

<pre>&lt;input id="nameOnCard"
&nbsp; &nbsp; &nbsp; name="nameOnCard"
&nbsp; &nbsp; &nbsp; <strong>ng-model="vm.creditCard.nameOnCard"</strong>
&nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; placeholder="Name on Card"
&nbsp; &nbsp; &nbsp; title="Name on Card"
&nbsp; &nbsp; &nbsp; type="text" /&gt;
</pre>

Locate the cardNumber input field and bind to the creditCard object by adding the following ng-model attribute.

<pre>&lt;input id="cardNumber"
&nbsp; &nbsp; &nbsp; name="cardNumber"
&nbsp; &nbsp; &nbsp; <strong>ng-model="vm.creditCard.cardNumber"</strong>
&nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; placeholder="Credit Card Number"
&nbsp; &nbsp; &nbsp; title="Credit Card Number"
&nbsp; &nbsp; &nbsp; type="text" /&gt;
</pre>

Locate the securityCode input field and bind to the creditCard object by adding the following ng-model attribute.

<pre>&lt;input id="securityCode"
&nbsp; &nbsp; &nbsp; name="securityCode"
&nbsp; &nbsp; &nbsp; <strong>ng-model="vm.creditCard.securityCode"</strong>
&nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; placeholder="Security Code"
&nbsp; &nbsp; &nbsp; title="Security Code"
&nbsp; &nbsp; &nbsp; type="text" /&gt;
</pre>

Locate the billingPostalCode input field and bind to the creditCard object by adding the following ng-model attribute.

<pre>&lt;input id="billingPostalCode"
&nbsp; &nbsp; &nbsp; name="billingPostalCode"
&nbsp; &nbsp; &nbsp; <strong>ng-model="vm.creditCard.billingPostalCode"</strong>
&nbsp; &nbsp; &nbsp; class="form-control"
&nbsp; &nbsp; &nbsp; placeholder="Billing Postal Code"
&nbsp; &nbsp; &nbsp; title="Billing Postal Code"
&nbsp; &nbsp; &nbsp; type="text" /&gt;
</pre>

At this point you can run the sample and you should see the credit card page displayed with the appropriate data in the drop-down lists.

Summary

In this blog post you created a new empty web application in Visual Studio, added Angular and Bootstrap to display a credit card page. You built a few different JavaScript files to build an Angular module and routes for the SPA. You then built a credit card controller which loads data into arrays for displaying in drop-down lists on the credit card page. You also setup the appropriate HTML to display error messages, a loading message, and the various input fields for accepting credit card information from a user. In the next post you will learn to get data from the Web API instead of using hard-coded data.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads . Choose the category "PDSA Blog", then locate the sample PDSA Blog Sample: Angular Credit Card Page – Part 1.

Plan Your Technology Team with This Online Assessment Tool

Subscribe to Email Updates

Build a Technology Dream Team. Start with This Online Assessment. →