Build a Credit Card Entry Page using Angular – Part 2

In the last blog post, you created an HTML page (Figure 1) to enter credit card information using Angular. You created some hard-coded functions in your Angular controller to populate the three drop-down lists. In this blog post, you create Web API calls to gather the data for these three drop-down lists from a SQL Server table. These Web API calls request the information for these drop-down lists from a view model class. The view model class uses the Entity Framework (EF) to build a collection credit card types from a SQL Server table, a collection of language-specific month names, and a collection of years. Once you have this built, you call the Web API from your Angular controller to load the drop-down lists from this data instead of the hard-coded data you used in the last blog post.

Figure 1: The Credit Card data entry page

Database Design

There are two tables (shown in Figure 2) needed for the credit card page. The first table, named CreditCardType, holds the list of credit card types (Visa, MasterCard, etc.) to be loaded into the drop-down on the web page. The second table, named CreditCard, holds the credit card information entered by the user, and will be used in the next blog post.

Figure 1: Two tables are needed for credit card information

To create these two tables, open SQL Server Management Studio and select an existing, or create a new database. Open a query window in your new database and type in the following script to create the CreditCard and CreditCardType table.

CREATE TABLE CreditCard (
	CreditCardId uniqueidentifier NOT NULL 
	   PRIMARY KEY NONCLUSTERED,
	CardType varchar(20) NOT NULL,
	NameOnCard varchar(100) NOT NULL,
	CardNumber varchar(25) NOT NULL,
	SecurityCode varchar(4) NOT NULL,
	ExpMonth smallint NOT NULL,
	ExpYear smallint NOT NULL,
	BillingPostalCode varchar(10) NOT NULL
);
CREATE TABLE CreditCardType (
	CardTypeId int IDENTITY(1,1) NOT NULL 
	  PRIMARY KEY NONCLUSTERED,
	CardType varchar(20) NOT NULL,
	IsActive bit NOT NULL DEFAULT ((1)) 
);
INSERT CreditCardType (CardType)
       VALUES ('Visa');
INSERT CreditCardType (CardType) 
       VALUES ('Master Card');
INSERT CreditCardType (CardType) 
       VALUES ('American Express');
INSERT CreditCardType (CardType) 
       VALUES ('Discover');
INSERT CreditCardType (CardType) 
       VALUES ('Diners Club');

Add the Entity Framework

I separate the classes for my data access into a separate DLL. This provides me with the ability to change my data access method later if I want. To do this, right mouse click on your solution and choose Add | New Project from the menu. Select Windows | Class Library from the list of templates. Set the Name to DataLayer and click the OK button. Delete the Class1.cs file from the project as this is not needed.

Right mouse click on the new DataLayer project and select Add | New Item from the menu. Select Data | ADO.NET Entity Data Model from the list of templates. Set the Name to PTC and click the Add button. Choose Code First from Database from the list. Create a new connection to the database that contains the CreditCard and CreditCardType tables you created earlier. Select both tables from the list and click the Finish button.

After the generation is complete, you have three new classes in your class library project. An App.config file is also created with a <connectionString> element. Move the connection string from the App.config in this project into the Web.config file in your CreditCardEntry project. Open the App.config file and locate the <connectionStrings> element that looks like the following:

<connectionStrings>
  <add name="PTC"
     connectionString="data source=localhost;
         initial catalog=PTC;
         integrated security=True;
         MultipleActiveResultSets=True;
         App=EntityFramework"
    providerName="System.Data.SqlClient" />
</connectionStrings>

Cut this out of the App.config file, open the Web.config file in the CreditCardEntry project and paste this section into this config file. After you have moved the connection string, delete the App.config file from the DataLayer project. Your data layer is now complete and ready to be used.

Add a View Model

Sticking with our theme of "separation of concerns", let’s build a view model class to use as the intermediary between the Web API controller and the data access layer. It is a good practice to keep as little code as possible in the controller. By creating a view model class in a separate project it allows you to reuse all the business and data access logic in any other project.

Right mouse click on your CreditCardEntry solution and choose Add | New Project from the menu. Select Windows | Class Library from the list of templates. Set the Name to ViewModelLayer and click the OK button. Rename the Class1.cs file to CreditCardViewModel.cs. Answer yes when prompted if you wish to rename the class as well.

Right mouse click on the References folder and select Add Reference from the menu. Click on the Projects | Solution tab and check the DataLayer from the list of projects. Click the OK button to add the reference.

Since you are going to be using Entity Framework generated classes in your view model class you need to add some EF references to this project. Right mouse click on the ViewModelLayer project and select Manage NuGet Packages from the menu. Click on the browse tab and type in Entity Framework into the search text box and hit the Enter key. Locate the EntityFramework by Microsoft and click on it. Click the Install button to add all of the appropriate references to the entity framework for this project.

MonthInfo Class

When loading the months into the drop-down list, you are going to need to know the month number as well as the month name. Create a class called MonthInfo in the ViewModelLayer project that you can place these two values into. A collection of these objects will be serialized and sent to Angular for loading into the drop-down.

public class MonthInfo
{
  public MonthInfo(short number, string name) {
    MonthNumber = number;
    MonthName = name;
  }
  public short MonthNumber { get; set; }
  public string MonthName { get; set; }
}

Add using Statements

The CreditCardViewModel class is going to call the CreditCardType class to retrieve the various credit card types to display. This class will retrieve validation error messages from your EF generated classes and generate a list of month names based on the user’s current browser language. With all of this functionality, and a few others, it will be necessary to add the following list of using statements at the top of the CreditCardViewModel file.

using DataLayer;
using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.Globalization;
using System.Linq;

Create Properties for View Model

Like any view model class, a set of properties are needed to hold the state of the object. Add the following properties to the CreditCardViewModel class.

public bool IsValid { get; set; }
public List<DbEntityValidationResult> Messages { get; set; }
public string DefaultLanguage { get; set; }
public string Language { get; set; }
public CreditCard Entity { get; set; }
public List<CreditCardType> CardTypes { get; set; }
public List<MonthInfo> Months { get; set; }
public List<int> Years { get; set; }

The Entity property holds the current CreditCard object the user is attempting to insert. An IsValid property is used to report back if there were validation errors in the current Entity. The Messages property is a list of DbEntityValidationResult objects. Each of these objects contains a validation error for a field that failed.

As there are three drop-down lists to load on your page, create three properties to hold a collection for each of these; CardTypes, Months and Years. For the months drop-down list, you should strive to display them in the user’s language. You can attempt to retrieve the user’s language from the browser and pass that to the Language property in the view model so it can use the Globalization classes in .NET to retrieve month names in the language of choice. However, if you are unable to retrieve the user’s language setting, then you should have a DefaultLanguage property you can fill in with a default language setting.

Initialize the Public Properties

It is always a good idea to initialize your properties in your view model class to a valid start state. Build a constructor in your view model class to set each property to an initialized state.

public CreditCardViewModel() {
  IsValid = true;
  Messages = new List<DbEntityValidationResult>();
  DefaultLanguage = string.Empty;
  Language = string.Empty;
  Entity = new CreditCard();
  CardTypes = new List<CreditCardType>();
  Months = new List<MonthInfo>();
  Years = new List<int>();
}

Load Card Types

In the last blog post, you hard-coded credit card types in the Angular controller. Now that you have a database table, and the appropriate EF classes, you can retrieve these credit card types from your table and populate your CardTypes property. Create a method called LoadCardTypes() and write the code shown below.

public void LoadCardTypes() {
  PTC db = new PTC();
  CardTypes = db.CreditCardTypes
              .Where(c => (c.IsActive))
              .OrderBy(c => c.CardType).ToList();
}

Load Months

Create a method named LoadMonths to build this collection of MonthInfo objects. Using the System.Globalization namespace you attempt to get the month names from the CultureInfo class in .NET. The DateTimeFormat property of the CultureInfo class contains a MonthNames collection with the localized month names for the culture specified by the language passed to the constructor of the CultureInfo class. If you pass a bad language specifier, an exception is thrown. You then use the value in the DefaultLanguage property. This DefaultLanguage property is filled in by the Web API controller with data retrieved from the Web.config file.

public void LoadMonths() {
  string[] monthNames = null;
  try {
    // Try to get month names
    monthNames = (new CultureInfo(Language))
                    .DateTimeFormat.MonthNames;
  }
  catch (CultureNotFoundException) {
    // Default to a known language
    monthNames = (new System.Globalization
                    .CultureInfo(DefaultLanguage))
                      .DateTimeFormat.MonthNames;
  }
  // Create Months Array
  for (int index = 0; index < monthNames.Length; index++) {
    // NOTE: Month array is 13 entries long
    if (!string.IsNullOrEmpty(monthNames[index])) {
      Months.Add(new MonthInfo(Convert.ToInt16(index + 1),
                               monthNames[index]));
    }
  }
  if (Entity.ExpMonth == 0) {
    // Figure out which month to select
    // Make it next month by default
    Entity.ExpMonth = Convert.ToInt16(DateTime.Now.Month + 1);
    Entity.ExpYear = Convert.ToInt16(DateTime.Now.Year);
    // If past December, then make it January of the next year
    if (Entity.ExpMonth > 12) {
      Entity.ExpMonth = 1;
      Entity.ExpYear += 1;
    }
  }
}

Once you have the array of month names, you need to turn these into a collection of MonthInfo objects. Loop through the array and each time through create a new instance of the MonthInfo class, setting the MonthNumber and the MonthName properties from the month names array.

The last thing to do is to default the ExpMonth and ExpYear to some default values. As you do not want your user to put in a month and year less than the current month and year, you add one to the current month and set that into the ExpMonth property. If you add one to the current month and it comes out to be 13, then increment the year by one and place that value into the ExpYear property. Otherwise, you just set the ExpYear to be the current year.

Load Years

The last method to create is to load the years into the Years property. The LoadYears method accepts a specified number of years in the future to load a generic list of integers. If you do not pass in any number, the default of 20 years is used.

public void LoadYears(int yearsInFuture = 20) {
  List<int> ret = new List<int>();
  Years = new List<int>();
  for (int i = DateTime.Now.Year; 
       i <= (DateTime.Now.Year + yearsInFuture); i++) {
    Years.Add(i);
  }
}

Build Web API Controllers

Now that you have a database design, some Entity Framework classes, and a view model to encapsulate the data you need for your credit card page, you are ready to expose that data through a web service. Right mouse click on the References folder in the CreditCardEntry project and select Add Reference. Add references to your two new projects; DataLayer and ViewModelLayer.

Credit Card Types Controller

Right mouse click on the Controllers folder and select Add | Web API Controller Class (v2.1). If this option does not appear in your drop down menu, select Add | New Item and choose it under the Web | Web API templates. Set the name of this controller to CreditCardTypeController. Remove all code within the class and write the following method.

public IHttpActionResult Get() {
  IHttpActionResult ret;
  CreditCardViewModel vm = new CreditCardViewModel();
  vm.LoadCardTypes();
  if (vm.CardTypes.Count() > 0) { 
    ret = Ok(vm.CardTypes);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

This method creates an instance of your CreditCardViewModel class. Call the LoadCardTypes method to load the CardTypes collection in the view model. If card types are loaded, return the status code of 200, via the Ok method, passing in the card types collection as the payload. If no card types are found, then return a 404 using the NotFound method.

Month Names Controller

Right mouse click on the Controllers folder and select Add | Web API Controller Class (v2.1). Set the name of this controller to MonthNamesController. Remove all code within the class and write the following method.

public IHttpActionResult Get(string id) {
  IHttpActionResult ret;
  CreditCardViewModel vm = new CreditCardViewModel();
  // Set default language
  vm.DefaultLanguage =
    ConfigurationManager.AppSettings["DefaultLanguage"];
  // Set the language passed in
  vm.Language = (string.IsNullOrEmpty(id) ? 
                 vm.DefaultLanguage : id);
  vm.LoadMonths();
  if (vm.Months.Count() > 0) {
    ret = Ok(vm.Months);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

This method is called from your Angular controller, but you need to pass in a parameter that is named id. You must use the parameter name of id since this is what the default route is expecting. The id parameter is the language code retrieved from the browser. By passing in the language you can let .NET return the month names in the appropriate language for your user. Call the LoadMonths method in your view model class and if months are loaded, return the months via the Ok method.

If no language is passed to this method, retrieve a default language from your <appSettings> section in the Web.config file and put it into the DefaultLanguage property of your view model. This default value will be used if no language is passed, or an unrecognized language is passed from the browser. Open your Web.Config file and add the DefaultLanguage key and a key for YearsInFuture. You will need the number of years in the next controller.

<appSettings>
  <add key="DefaultLanguage"
        value="en-US" />
  <add key="YearsInFuture"
        value="20" />
</appSettings>

Years Controller

Right mouse click on the Controllers folder and select Add | Web API Controller Class (v2.1). Set the name of this controller to YearsController. Remove all code within the class and write the following method.

public IHttpActionResult Get() {
  IHttpActionResult ret;
  CreditCardViewModel vm = new CreditCardViewModel();
  vm.LoadYears(
    Convert.ToInt32(
      ConfigurationManager.AppSettings["YearsInFuture"]));
      
  if (vm.Years.Count() > 0) { 
    ret = Ok(vm.Years);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

This method retrieves the YearsInFuture setting from the Web.config file and passes that value to the LoadYears method in your view model class. The Years collection is filled with the number of years specified by the value passed in. If there are years in the Years collection, they are returned via the Ok method.

Call Web API to Load Drop-Down Lists

Now that you have the Web API calls built for loading the drop-down lists, you can now call these from your Angular controller. All the hard-coded functions you wrote in the previous blog post are going to be rewritten to call the appropriate Web API methods. You also need to add some exception handling to report any errors. Open the \app\creditcard\creditcard.controller.js file and start adding this new functionality.

Handle Exceptions

When you make calls to a Web API you should always make sure you are checking for exceptions. Write a generic handleException function to retrieve any error message information and place an object with a single property called message into the vm.uiState.messages array.

function handleException(error) {
  vm.uiState.isMessageAreaHidden = false;
  vm.uiState.isLoading = false;
  vm.uiState.messages = [];
  switch (error.status) {
    case 404:  // 'Not Found'
      vm.uiState.messages.push(
        {
          message: "The data you were " +
                    "requesting could not be found"
        });
      break;
    case 500:  // 'Internal Error'
      vm.uiState.messages.push(
        {
          message: error.data.exceptionMessage
        });
      break;
    default:
      vm.uiState.messages.push(
        {
          message: "Status: " +
                  error.status +
                  " - Error Message: " +
                  error.statusText
        });
      break;
  }
}

Load Card Types

In Part 1 of this blog post you hard coded a set of credit card types in the loadCardTypes function. Locate the loadCardTypes function in the creditcard.controller.js file and modify the code to look like the following.

function loadCardTypes() {
  dataService.get("/api/CreditCardType")
    .then(function (result) {
      vm.cardTypes = result.data;
      if (vm.cardTypes.length > 0) {
        vm.selectedCardType = vm.cardTypes[0];
      }
    },
    function (error) {
      handleException(error);
    });
}

In Part 1 of this blog post, the $http data service was passed into the controller. We assigned this service to the variable named dataService. Use the get() function of this data service to call the CreditCardType controller you created earlier to retrieve the credit card types from your SQL Server database. Once you retrieve the card type from the Web API, the result.data property is filled in with an array of JSON objects that represent each card type. Assign these values to the vm.cardTypes property because this is the property that is bound to the HTML <select> element that displays them to the user. Finally set the vm.selectedCardType property to the first element in the array in order to position the <select> element to that card type.

Load Month Names

The month names are still hard-coded in the loadMonths function. Locate the loadMonths function and replace the hard-coding with the code to call the Web API you created.

function loadMonths() {
  var today = new Date();
  // Get the language from the browser
  var language = 
     navigator.languages && 
     navigator.languages[0] || // Chrome / Firefox
     navigator.language ||     // All browsers
     navigator.userLanguage;   // IE <= 10
  dataService.get("/api/MonthNames/" + language)
    .then(function (result) {
      // Transform the data to use nn - monthName format
      for (var index = 0; 
               index < result.data.length; 
               index++) {
        var month = {
          monthNumber: index + 1,
          monthName: (index + 1).toString() 
            + "-" + result.data[index].monthName
        };
        vm.months.push(month);
      }
      // Figure out which month to select
      // Make it next month by default
      vm.creditCard.expMonth = today.getMonth() + 2;
      // If past December, make it January of next year
      if (vm.creditCard.expMonth > 12) {
        vm.creditCard.expMonth = 1;
        vm.creditCard.expYear = vm.creditCard.expYear + 1;
      }
      vm.selectedMonth = 
        vm.months[vm.creditCard.expMonth - 1];
      vm.uiState.isLoading = false;
    },
    function (error) {
      handleException(error);
    });
}

The call to the Web API is like the other calls you just wrote except you must pass in the current language the browser is running. The navigator object is queried to see which of the languages, language or the userLanguage properties contain a value. This is the value that you pass to the id parameter of the MonthNames controller. When the data is returned, loop through each object and convert the data to display in a nn-monthName format, for example; 1-January, 2-February, etc. The rest of the code is what you wrote in the first part of this blog post to set the default month and year of the drop-downs.

Load Years

The loadYears JavaScript function you wrote in the last blog post is almost the same as the code you wrote in the Years controller. Having the functionality to load years, months and credit card types in a web service gives us more flexibility rather than hard-coding everything in JavaScript files. Assign the return value from the Years API to the vm.years property as this is the property that is bound to the <select> element used to display the years.

function loadYears() {
  var year = new Date().getFullYear();
  dataService.get("/api/Years")
    .then(function (result) {
      vm.years = result.data;
      vm.creditCard.expYear = year;
    },
    function (error) {
      handleException(error);
    });
}

You can now run the sample and see all of the data coming from the Web API calls.

Summary

In this blog post you created the appropriate Web API calls to load each of the drop-down lists on your credit card data entry page. You built a set of Entity Framework classes to read the credit card types from a SQL Server table. You built three Web API controllers to be called from your Angular functions to load each of the drop-down lists. Finally, you replaced the hard-coded functions you wrote in the first blog post, with calls to the Web API to get all data from the back-end server. In the next blog post, you are going to take the credit card data entered by the user, validate that data, and then save that data into a SQL Server table.

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 2.

Build a Credit Card Entry Page using Angular – Part 1

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.

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.

Figure 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.

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:

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

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.

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.

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

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.

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.

(function () {
  'use strict';
  angular.module('app', ['ngRoute']);
})();

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.

(function () {
  'use strict';
  angular.module('app').controller('IndexController',
    IndexController);
  function IndexController() {
  }
})();

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.

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

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.

<p>This sample illustrates a credit card entry system.</p>

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

  <script src="scripts/angular.js"></script>
  <script src="scripts/angular-route.js"></script>

  <script src="app/index/index.module.js"></script>
  <script src="app/index/index.controller.js"></script>
  <script src="app/index/index.route.js"></script>
</body>
</html>

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.

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

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.

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

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.

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

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.

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

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.

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

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

  <script src="scripts/angular.js"></script>
  <script src="scripts/angular-route.js"></script>
  <script src="app/index/index.module.js"></script>
  <script src="app/index/index.controller.js"></script>
  <script src="app/index/index.route.js"></script>

  <script src="app/creditcard/creditcard.controller.js">
  </script>
</body>
</html>

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.

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

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.

vm.cardTypes = [];
vm.months = [];
vm.years = [];

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.

vm.selectedMonth = {};
vm.selectedCardType = {};

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.

vm.creditCard = {
  creditCardId: null,
  cardType: null,
  nameOnCard: null,
  cardNumber: null,
  securityCode: null,
  expMonth: null,
  expYear: null,
  billingPostalCode: null
};

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:

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

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-model and 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.

<select id="types"
        name="types"
        class="form-control"
        ng-model="vm.selectedCardType"
        ng-options="item.cardType for item in vm.cardTypes 
                    track by item.cardType">
</select>

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.

foreach (item in vm.cardTypes) {
  <option value=”item.cardType”>item.cardType</option>
}

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.

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

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

<select id="expYears"
        name="expYears"
        class="form-control"
        ng-model="vm.creditCard.expYear"
        ng-options="item for item in vm.years track by item">
</select>

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.

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

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

<select id="expMonths"
        name="expMonths"
        class="form-control"
        ng-model="vm.selectedMonth"
        ng-options="item.monthName for item 
                    in vm.months 
                    track by item.monthNumber">
</select>

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.

<input type="hidden" 
       ng-model="vm.creditCard.creditCardId" />

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

<input id="nameOnCard"
       name="nameOnCard"
       ng-model="vm.creditCard.nameOnCard"
       class="form-control"
       placeholder="Name on Card"
       title="Name on Card"
       type="text" />

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

<input id="cardNumber"
       name="cardNumber"
       ng-model="vm.creditCard.cardNumber"
       class="form-control"
       placeholder="Credit Card Number"
       title="Credit Card Number"
       type="text" />

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

<input id="securityCode"
       name="securityCode"
       ng-model="vm.creditCard.securityCode"
       class="form-control"
       placeholder="Security Code"
       title="Security Code"
       type="text" />

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

<input id="billingPostalCode"
       name="billingPostalCode"
       ng-model="vm.creditCard.billingPostalCode"
       class="form-control"
       placeholder="Billing Postal Code"
       title="Billing Postal Code"
       type="text" />

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.

The Importance of a CIO

CIO Services Are you without a CIO in your company? Did you know that a good CIO can save you money and keep your projects on-time and on-budget. What are some of the key duties a CIO performs that helps a company achieve its goals?

IT Assessment

A continuous review of your business and IT organization to determine where they are aligned and where they are mismatched.

IT Roadmap

Lay out the future vision of where your IT organization needs to be in order to be aligned with your company's financial and strategic goals.

IT Executive Mentoring

Be the go-between the executive team and the IT team. Ensure that the projects being worked on are in alignment with what the business needs.

Application Lifecycle Management

Look at IT processes and personnel to identify areas where those processes might need a little more guidance. Provide a comprehensive guide to how all IT projects should be run.

More Information?

If you would like more information on how a CIO can help you visit our page here. Call us for a complimentary assessment and how our Virtual CIO service can assist your company keep your IT projects on-time and on-budget.

Everyone Needs Help Now and Then...

Professional MentoringYes, you can google for answers to tricky programming problems. However, when you get back 5 different answers, how do you know which one is the best one to use? This is where PDSA, Inc. comes in. We have over 270+ man years of development expertise among our developers. We use tried and true techniques every day in our consulting business to solve many of the same business programming problems that you are facing. When you engage with PDSA, you aren't just getting one expert, you are getting a whole company of experts. 

Unlike tech support from a company like Microsoft where you pay for help whether or not they solve your problem, with PDSA you only pay if we solve your problem. We also bill in 15 minute increments instead of for a whole hour. This will save you money, and most importantly, time. We have been successfully mentoring with many of our clients for years. They use us periodically as needed to answer their tough questions, or just to bounce ideas of us. However you choose to use us, know we have your best interests in mind. We have been around for over 25 years and we are proud of the fact that we have so many repeat customers. We are successful because we make you successful.

For more information on our mentoring services, visit our Mentoring page at http://www.pdsa.com/Mentoring.

Call us today at (714) 734-9792 and ask for Michael Krasowski or David Lefort. We will then get you in touch with one of our senior technicians that can assist you with your mentoring questions.

MVC, Web Forms, or Client-Side Technologies?

It seems like everywhere you read, everyone is talking about using Angular, Bootstrap, or MVC to create web applications. But what about programmers still working in Web Forms? You want to continue to use Web Forms and still build mobile web apps, use jQuery, Angular and Bootstrap. Not to fear, Web Forms is not dead, and nearly everything you can do in MVC, you can also do in Web Forms. Web Forms can also use client-side frameworks just as well as MVC.

MVC Advantages

Many programmers feel they have to choose one technology over another. But, there is really no reason you can’t program in both MVC and Web Forms and use each for what they are good at. Many MVC programmers give many reasons why you should use MVC as opposed to Web Forms. The reasons I hear more often are:

  1. Fine control over the HTML generated
  2. Ability to unit test
  3. Can control the "id" attribute generated
  4. Easier to use jQuery and JavaScript because no generated "id" attribute
  5. Ability to use Friendly URLs
  6. No ViewState sent to client

Web Forms Advantages

Of course, programmers that like Web Forms have many reasons why they want to stick with it:

  1. Rapid Application Development
  2. Less code to write because of rich server controls
  3. Lots of server controls to choose from
  4. Great third-party support
  5. Easy to preserve state
  6. They like the event driven model
  7. Hides the complexity of the web

Client-Side Advantages

Of course there are many advantages to using client-side code as well.

  1. Very fast
  2. Greatly reduces the need for post-backs
  3. No hiding of web behind any server-side technologies
  4. Lots of frameworks to help you develop

Which One to Choose?

Each of the technologies above has disadvantages as well. The best part is, you do not need to just choose one. You can have the majority of your web application written in Web Forms and still start using client-side technologies like jQuery and Angular. You can also start adding new pages in MVC and these can integrate with your Web Forms pages with no problems.

Almost all of the advantages outlined above for using MVC can also be accomplished in Web Forms as well. You might have to slightly modify the way you work in Web Forms, but it can be done. Both technologies use ASP.NET, so the engine is the same. If you wish to just stick with Web Forms, then do that. If you want to learn more about having Web Forms take advantage of MVC-like features, read my old blog post called Web Forms is Not Dead located at http://bit.ly/2ehCTP1.

Using Angular

Many Web Forms developers ask about Angular and using it in their Web Forms apps. The great thing about Angular is you can easily add it to your Web Forms or MVC application. You will most typically use Angular for new pages that you wish to create in your existing application, or to replace slow performing pages. This means you will probably start with a plain old HTML page and then add your Angular code to that.

Summary

It is up to you which technologies you are going to use when developing your web applications. Using ASP.NET Web Forms or MVC are both good choices and both can be made to run quickly or slowly depending on how you code it. Taking advantage of client-side technologies such as Angular and jQuery can give you a performance boost. If you need help with any migration or learning any of these technologies, please contact PDSA and we will be glad to help.

More Angular Routing Topics

In the last blog post (Introduction to Angular Routing) you were introduced to routing in Angular (v1.x). You learned the basics of how to route to different HTML fragments using the routing features of Angular. In this blog post you learn more things you can do with routing such as specifying the controller to use, aliasing the controller using “as”, passing parameters to a page and programmatically calling a route.

Set the Controller Name

If the target page for your route has some functionality that needs to be performed, you will have a controller associated with that page. An additional property you can set on the route object when configuring your routes is the controller property. Set this property to the name of the controller function for that page.

.when('/page2/',
{
  templateUrl: 'page2.template.html',
  controller: 'Page2Controller'
})
When using the controller property in your route, you must include the .js file that contains the code for that controller on your main HTML page.
<script src="app.module.js"></script>
<script src="index.controller.js"></script>
<script src="page2.controller.js"></script>
Below is the code for the Page2Controller. This controller is a simple sample that just sets a message property with some hard-coded text using the $scope application object.
(function () {
  'use strict';
  
  angular.module('app')
   .controller('Page2Controller', Page2Controller);
  function Page2Controller($scope) {
    $scope.message = 'Page 2 says \'Hello\'';
  }
})();
Once the message property is set on the $scope object, that value can be displayed on the HTML fragment using the data binding syntax.
<h2>{{message}}</h2>

Alias the Controller

When developing Angular pages, it is always a good idea to alias your controller using the “as” syntax as shown in the following code snippet.
<div ng-app="app"
      ng-controller="IndexController as vm"
However, this syntax is only used when you are setting the ng-controller directive on your main page. On your page fragments you are not using this directive. Not to worry, the route object supports a controllerAs property so you can set this alias name.
.when('/page1',
{
  templateUrl: 'page1.template.html',
  controllerAs: 'page1',
  controller: 'Page1Controller'
})
.when('/page2',
{
  templateUrl: 'page2.template.html',
  controllerAs: 'page2',
  controller: 'Page2Controller'
})
When you use the "as" syntax, the application scope is passed into your controller as ‘this’. Assign 'this' to a local variable in your controller, then create any properties you want on the application scope so you can use those properties in your HTML. In the Page1Controller, shown below, create a property named message and set it to a hard-coded string.
(function () {
  'use strict';
  
  angular.module('app')
   .controller('Page1Controller', Page1Controller);
  function Page1Controller() {
    var vm = this;
    vm.message = 'Hello from Page One!';
  }
})();
To use the message property on your HTML page, prefix the property name with the value you set in the controllerAs property. To display the value in the message property in your page1.template.html page you use the following code.
<p>{{page1.message}}</p>
Again, if you are specifying controllers in your routing, make sure you include the appropriate .js files on your index.html page.
<script src="app.module.js"></script>
<script src="index.controller.js"></script>
<script src="page1.controller.js"></script>
<script src="page2.controller.js"></script>

Pass a Parameter to a Route

Sometimes you need to pass a parameter to a page. You can also pass parameters to a route. In the anchor tag shown below, you are passing a value of 12345 to the route defined by /page2.
<a href="#/page2/12345" 
   class="btn btn-primary">
  Page 2 with parameter
</a>
Keep your original /page2 route, but add an additional route using a when() function. After the /page2 add another forward slash followed by a colon (:) and a variable name such as id.
.when('/page2',
{
  templateUrl: 'page2.template.html',
  controllerAs: 'page2',
  controller: 'Page2Controller'
})
.when('/page2/:id',
{
  templateUrl: 'page2.template.html',
  controllerAs: 'page2',
  controller: 'Page2Controller'
})
In your Page2Controller function, you now need to have the $routeParams injected. Add the $routeParams service as a parameter to your Page2Controller function. Before you use the parameter, test to see if the value was passed in by using an if statement as shown in the following code.
function Page2Controller($routeParams) {
  var vm = this;
  if ($routeParams.id) {
    vm.message = 'The id passed in was: '
                 + $routeParams.id;
  }
  else {
    vm.message = 'Page 2 says \'Hello\'';
  }
}
If you need more than one value passed, just add another route such as the following:
.when('/page2/:id/:extraText',
{
  templateUrl: 'page2.template.html',
  controllerAs: 'page2',
  controller: 'Page2Controller'
})
In your index.html, you call this route using the following:
<a href="#/page2/123/abc" 
   class="btn btn-primary">
  Page 2 with 2 parameters
</a>
In the controller for this page, add another test for this additional parameter.
if ($routeParams.extraText) {
  vm.message = 'extraText=' + $routeParams.extraText;
}

Programmatically Calling a Route

Many times, when running code in one of your controllers, you will have a need to redirect the user to a new route. This is accomplished by calling the path() function on the $location service and then passing in one of your defined routes. The following code shows a couple of functions you may have in a controller. If either of these functions are called, the URL of the browser is set to the new path, and then the user is directed to the appropriate page. Make sure you add the $location service as a parameter to your controller function.
function IndexController($location) {
  function goToPage1() {
    $location.path("/page1");
  }
  function goToPage2() {
    $location.path("/page2");
  }
}

Summary

In this blog post you learned how to route to page fragments, but specify the controller to use with that page. The controller and controllerAs properties of the route object as used to set the information for the controller to be used with a page. You are able to add one or more parameters to a route. The $routeParams service can retrieve as many different parameters passed to a controller as you want. Use the $location service to programmatically direct a user to a route in your Angular application.

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: More Angular Routing Topics.

Introduction to Angular Routing

To build a Single-Page Application (SPA) using Angular (v1.x), you typically build a single HTML page and inject HTML fragments within this one page as the user navigates within your application. Navigation in Angular employs a mechanism called routing. This blog post explores how to perform navigation within a SPA using Angular routing.

Server-Side Development

As you make the transition from server-side development to client-side development, you will find many of the same concepts that you employ on the server-side have equivalents on the client-side. Of course, they are done differently, but the concepts are there nonetheless.

When developing server-side web applications with MVC or Web Forms, you use a common layout page for all of the standard “chrome” you want around your content. This chrome is your header, footer and maybe a sidebar. The header consists of a menu system and maybe some graphics. The footer might have a copyright and some additional links. You do not want to duplicate the header and footer on each page in your application as that is a maintenance nightmare. Instead, each MVC or Web Forms application has a special “layout” page where you create the chrome. You create your content pages with the HTML fragments you wish to display to the user, and inject those fragments within the layout page.

MVC has a special page named _Layout.cshtml located under the \Shared folder (Figure 1). This page has a piece of Razor code called @RenderBody() which tells MVC into where to inject your fragments of HTML and Razor code. Web Forms uses a concept called a “Master Page”, shown in Figure 2 as Site.Master, which uses a <asp:ContentPlaceHolder /> control to specify where to inject your content pages.

Both these approaches keep the chrome for your web application in a single location. This makes changes to your website easy to accomplish. When creating a SPA using Angular (or any other client-side framework), you should strive to use this same technique.


Figure 1: MVC uses a shared layout page



Figure 2: Web Forms uses a master page

Angular ng-view Directive

Angular has the same mechanism for defining a HTML page with the chrome, and a directive for specifying where to inject the HTML fragments that make up each content page. You typically create an index.html page with the chrome and a single <div> tag that uses the Angular directive ng-view (Figure 3). This directive is what is used to specify the location in which to inject the content pages.
It is important to note that only one instance of ng-view may be used in your Angular application. In other words, you cannot nest an ng-view within another ng-view. If you are using this approach correctly, you shouldn’t have to nest ng-view anyway.


Figure 3: Angular uses any html page for the chrome

Download Routing JavaScript File

In order to use Angular routing, download the angular-route.js file into your project. If you are using Visual Studio, you may use the NuGet Package Manager to search for and install the AngularJS.Route package. Or, open a browser and navigate to https://code.angularjs.org/1.5.8/ and download the angular.route.js file from there. Either way, once you download this script file you will want to reference it from your main HTML page. Be sure to place it after you have referenced the angular.js file as shown in the following code snippet.
<script src="scripts/angular.js"></script>
<script src="scripts/angular-route.js"></script>

Declare Your Intention to use Routing

The first step in any Angular application is to define a module that is the main entry point for your application. You typically define a module using the following code.
(function () {
  'use strict';
  angular.module('app', []);
})();
As you are going to be using routing in your Angular application, this is now a dependency that you need to tell Angular about. The second parameter to the module() is an array of strings for you to specify the names of any dependencies needed for your application. In the code snippet below you are passing in a single element array with the value being ‘ngRoute’. The ‘ngRoute’ value is defined as a provider in the angular-route.js file you downloaded and included in your project.
(function () {
  'use strict';
  angular.module('app', ['ngRoute']);
})();

The HTML Page

The complete HTML page, index.html, is shown in Listing 1. This page has a couple of anchor tags <a> that are used for our routing sample. In addition the <div ng-view> element is also defined within a Bootstrap row and column. This is where all HTML fragments will be displayed when you route to a new path.
<!doctype html>
<html>
<head>
  <title>Routing Sample</title>
  <link href="Content/bootstrap.min.css" 
        rel="stylesheet" />
</head>
<body>
  <div ng-app="app"
       ng-controller="IndexController as vm"
       class="container">
    <div class="row">
      <div class="col-sm-12">
        <a href="#/page1" 
           class="btn btn-primary">Page 1</a>
        <a href="#/page2" 
           class="btn btn-primary">Page 2</a>
      </div>
    </div>
    <br />
    <div class="row">
      <div class="col-sm-12">
        <div ng-view></div>
      </div>
    </div>
  </div>
  <script src="scripts/angular.js">
  </script>
  <script src="scripts/angular-route.js">
  </script>
  <script src="app.module.js"></script>
  <script src="index.controller.js">
  </script>
</body>
</html>
Listing 1: The HTML page for our routing sample

The index.controller.js file that is referenced in the above web page is an empty function as there is no functionality needed for this sample web page. The contents of the index.controller.js file is shown below just for completeness.
(function () {
  'use strict';
  
  angular.module('app')
   .controller('IndexController', 
               IndexController);
  function IndexController() {    
  }
})();

Define Your Routes

After you have told Angular that you are using routing, it is now time to create some routes. Within the app.module.js file (or create another file called index.route.js) add the code shown in Listing 2.
angular.module('app')
.config(function ($routeProvider) {
  $routeProvider
  .when('/',
  {
    template: ''
  })
  .when('/page1',
  {
    template: '<p>This is some text for Page1</p>'
  })
  .when('/page2',
  {
    template: '<h2>Page 2</h2>'
  });
});
Listing 2: Define routes to your Angular application.

The code in Listing 2 retrieves the module named 'app' from Angular. It then chains the config() function to instantiate the routes for the module. Pass in your custom function to the config() function. This function is passed the $routeProvider provider, which is defined in the angular-route.js file. You use this $routeProvider variable to define the routes you wish to configure using the when() function. Each when() function is passed two parameters. The first parameter is a string that is matched up with the path you define in your web page. For example, <a href="#/page1" …/> matches up with when('/page1'). The # symbol is used so the browser does not try to navigate to a page. Angular looks for anything that starts with “#/” and knows that you are using a route. Figure 4 shows the index.html page and the code in the $routeProvider definition and how they match up.


Figure 4: Use a # symbol to specify a route

The second parameter to the when() function is an Angular route object. This object has several properties that can be set. For this initial sample the template property is set. The template property lets you define any HTML code you wish to display in the ng-view directive when this route is invoked. In Figure 5 and Figure 6 you can see both the final web page and the corresponding code in the $routeProvider which caused that HTML to be displayed.


Figure 5: Page 1 shows some standard text


Figure 6: Page 2 shows some bold text

Handling a Bad Link

If you have a link in your HTML page that does not have a corresponding when() function configured, you should display some error text to your user. After the last when() function is called, add an otherwise() function as shown in the following code snippet.
.otherwise(
{
  template: '<h2>Bad Link!</h2>'
});

Avoid Hard-Coding HTML

The problem with the previous example is you hard-coded some HTML within your JavaScript. It is a best practice to keep all your HTML in .html files in your project. Instead of defining your routes using the template property, use the templateUrl property instead as shown in Listing 3. The templateUrl property must be set with the full path in relation to the index.html page. In this sample, all the .html pages are in the same folder. However, you might need to specify something like the following: templateUrl: 'app/templates/page1.template.html'.
angular.module('app')
.config(function ($routeProvider) {
  $routeProvider
  .when('/',
  {
    template: ''
  })
  .when('/page1',
  {
    templateUrl: 'page1.template.html'
  })
  .when('/page2',
  {
    templateUrl: 'page2.template.html'
  })
  .when('/error',
  {
    templateUrl: 'badlink.template.html'
  })
  .otherwise(
  {
    redirectTo: '/error'
  });
});
Listing 3: Use the templateUrl property to keep HTML code in .html files

In Listing 3 another new property was introduced in the otherwise() function, redirectTo. The redirectTo property allows you to redirect to another route. This property is most typically used in the otherwise() function. In the sample in Listing 3 if you attempted to go to a href such as the one shown in the following code snippet, you would be redirected to the path /error, which would then display the HTML in the badlink.template.html file.
<a href="#/badLink" 
   class="btn btn-primary">
     Bad Link
</a>
Another option for the otherwise() function is to simply specify a string as the first parameter. If a string is specified instead of an object, it interprets the string as a redirect to.
.otherwise('/error')

Summary

In this blog post you learned the basics of routing in Angular. Just like server-side web development, the concept of having a single place for all your chrome is also present client-side. The ng-view directive is used to specify where in your HTML page you wish to display other HTML pages. You will need to download angular-route.js in order to use routing in your Angular web pages.

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: Introduction to Angular Routing.

If You Fail to Plan, You Plan to Fail

IT Plan If you do not have a set of guidelines, templates and documents which describe and assist you in the many phases of a software project and the many challenges of running an IT organization then what are you doing in IT management? You must have these items in place in your IT organization in order to run a lean, efficient, on-time and on-budget shop. There are several items you must have in order to have a successful development team.

  • Application Lifecycle Management
  • Architectural Guidance
  • Management and Human Resource Practices

Check out our special report and see the kinds of guidelines, templates and documents you must have for each of the above items.

You Should be Using Bootstrap

Bootstrap Most users are browsing your website using a mobile device. Does your website look good? If not, then you should consider using Bootstrap for your CSS.

What is Bootstrap?

Bootstrap is a set of CSS and jQuery that helps you create responsive web pages. Responsive means that the styles of your website change depending on the device the user is browsing it from. This keeps your website looking good regardless of how the user browses to it.

How Does it Work?

Bootstrap's CSS changes on the fly based on the size of the browser. It uses what are called Media Queries which are a part of the CSS standard. A media query reads meta-data from the browser, then changes the styles to something that works based on the height, width or other browser information. For example, on a large desktop browser, a user might see columns of data across the page. However, on a mobile browsers, the user would see that same data, but going down the page. There is no additional coding your programmer has to do, this is all done just by using the appropriate Bootstrap styles.

How To Get Started

To ensure your website is responsive, you need a programmer or web designer that is familiar with Bootstrap. That person will make appropriate changes to each individual page to ensure it looks good on all types of different browsers. Yes, this will take time, but the end result is well worth the time and expense.

PDSA Can Help

Here at PDSA we have been using Bootstrap since the first version. We have successfully created many responsive websites for our clients. Contact us today for a complimentary analysis and proposal on ensuring your website can be viewed from all of today's mobile devices. Call us at (714) 734-9792 and ask for David Lefort or Michael Krasowski.

Generate Better Code, Faster!

Code Generation Are you or your developers writing stored procedures by hand? Are they creating duplicate CRUD (Create Read Update Delete) logic in multiple places? If so, you should consider a code generator.

Advantages of using a Code Generator

The benefits of using a code generator, such as the PDSA Haystack Code Generator to generate your data access logic are huge! If you are not using one, you need to ask yourself why? If the answer is because you think no one else can write code the way you can, that is not a good enough reason. When you consider how long it takes you to create, test and debug data access code, and how much time you put in when the schema of a table changes, you will soon find out that you are wasting a lot of time. The major reasons for using code generation are as follows:

  • Generation of INSERT, UPDATE and DELETE statements
  • Generation of stored procedures to do INSERT, UPDATE and DELETE
  • Bullet proof code for doing the Create, Read, Update, Delete (CRUD) logic
  • Ability to regenerate CRUD classes if schema changes
  • Consistency of code from one application to another
  • Moves SQL out of the UI tier and into a middle tier where it belongs
  • Moves the dependence on a particular database provider out of the UI
  • Reduces code complexity in the UI layer
  • Ability to map columns to properties in a class
  • Provide IntelliSense of tables, stored procedures, views and columns
  • Provides strong-typing of data in one location
  • Ability to customize templates to fit "your" style
  • Abstracts and encapsulates all data access code
  • Ability to separate programmers working on UI and data access

How to Pick a Code Generator

Before you start using a code generator you need to find one that allows you to do all of the things listed above. Be sure you can quickly and easily modify the code that is generated. In addition looks for one that supports generating multiple UI layers such as WPF, Angular, MVC, etc. The ability to generate a SOA layer such as Web API calls or WCF is an added bonus. 

Summary

How you create your CRUD logic needs to be thought out just as much as how you design the rest of your application architecture. Making many small classes/methods and then combining those together produces much more robust and maintainable code. Sure you end up with more classes, but these can be move to other assemblies so you don’t have to see all those files. You will definitely benefit in the long run. Using a code generator can really help speed up the development and ensure quality when developing the CRUD logic for your database application.

There are several code generators available for you to use. PDSA has the Haystack Code Generator (www.pdsa.com/haystack). The Haystack Code Generator does support all of the items listed above including the ones listed in how to pick a code generator.

Visit www.pdsa.com/haystack to learn more about Haystack and to download a free trial version.

Click here to read a special report on what code generation can do for you.