8 Questions to Ask to Determine if you should be in the Cloud

Everyone wonders if they should be moving to the cloud. Here are 8 starter questions you should ask yourself. Are you...


  1. Spending too much time on your IT infrastructure?
  2. Anticipating future growth?
  3. Needing to react quickly to change?
  4. Tired of hassling with Microsoft Exchange?
  5. Wanting to run your software as a service?
  6. Constantly adding more hardware to run your company
  7. Worried that your servers may not be backed up properly?
  8. Concerned about disaster recovery?

If you answered "Yes" to even one of these questions, a cloud solution may be just what you need. Companies are successfully using the cloud to scale their applications up and down without purchasing more equipment for the organization. Moving your applications into the cloud offers multiple benefits including:

  • Better security
  • Less manpower needed to manage software updates
  • More flexibility in application management
  • Disaster recovery is easier

If any of these keep you up at night, you should consider moving into the cloud. Fairway Technologies can help you migrate your infrastructure and your software to the cloud. Read more in our special report entitled Why Move to the Cloud?

Using MVVM in MVC Applications – Part 4

This blog post continues from the last two blog posts that you can find here.

This post is going to finish the MVC application using a MVVM approach. You are going to build the methods to select a single product from the product table. You are going to learn to update an existing product. You are also going to delete a product. Finally you learn how to handle server-side validation, and return validation messages back to the client to display to the user.

Get a Single Product

Prior to updating a record, you should go back to the table and retrieve the most current information. To do this, you require the primary key of the product record. This means you need to create a new property in your ProductViewModel class to hold this data. You also need to put the primary key value into a data- attribute, then take that value and place it into a hidden field on your page so it can be bound to the view model.

Open ProductViewModel and add a new property named EventArgument. It is in this property you are going to place the primary key value.

public string EventArgument { get; set; }

Modify the Init() method to initialize this property.

EventArgument = string.Empty;

Open the _ProductList.cshtml page

Add a new column header before all the other column headers in the <thead> area.

<th>Edit</th>

Now add a new column within the @foreach() loop.

<td>
  <a href="#" 
     data-pdsa-action="edit"
     data-pdsa-arg="@item.ProductId"
     class="btn btn-default btn-sm">
    <i class="glyphicon glyphicon-edit"></i>
  </a>
</td>

Notice the new data-pdsa-arg attribute. This attribute's value is filled in with the primary key of the table. You need to store this value into the EventArgument property you just added to the view model class. Open the Product.cshtml page and add a new hidden field below the EventAction hidden field.

@Html.HiddenFor(m => m.EventArgument, new { data_val = "false" })

To add the product id to the EventArgument hidden field, open the pdsa-action.js script file and add the following line.

$("#EventArgument").val($(this).data("pdsa-arg"));

If you ran the application right now and clicked on one of the edit buttons you would see that the EventArgument property of the view model is filled in.

Write GetEntity Method

Before you update a product, it is a good idea to retrieve the complete record from the table to ensure you have the latest values. Write a GetEntity method to use the EventArgument and set the Entity property with the Product returned from the Find method of the Products collection.

protected virtual void GetEntity() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Get the entity
    if (!string.IsNullOrEmpty(EventArgument)) {
      Entity = 
       db.Products.Find(Convert.ToInt32(EventArgument));
    }
  }
  catch (Exception ex) {
    Publish(ex, "Error Retrieving Product With ID=" 
                 + EventArgument);
  }
}

Now modify the "edit" case statement in the HandleRequest to call this new method.

case "edit":
  IsValid = true;
  PageMode = PDSAPageModeEnum.Edit;
  GetEntity();
  break;

Once the Entity property is set, you display the product detail page and the data from the item selected in the list is displayed in the fields. Run the page right now and verify that you are retrieving the correct product and displaying that product in the fields on the detail page.

Update the Product

Now that you have retrieved a product from the database and displayed it on the detail page, let's save changes back to the Product table. Add a using statement at the top of the ProductViewModel class. You need this namespace so you have access to the DbEntityValidationException class.

using System.Data.Entity;

Modify the Update method in the ProductViewModel class to look like the following:

protected void Update() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Do editing here
    db.Entry(Entity).State = EntityState.Modified;
    db.SaveChanges();
    PageMode = PDSAPageModeEnum.List;
  }
  catch (DbEntityValidationException ex) {
    IsValid = false;
    ValidationErrorsToMessages(ex);
  }
  catch (Exception ex) {
    Publish(ex, "Error Updating Product With ID=" 
                 + Entity.ProductId.ToString());
  }
}

Run the product page and change some of the product data. Press the Save button and you should see the list refreshed with the changes you made to the one record.

Delete a Product

Now that you have added and updated a product, you should now allow the user to delete a product. To delete a product, add a button to the HTML table next to each product. Open the _ProductList.html page and add a new column header in the <thead> area.

<th>Delete</th>

Next, add a column as the last column within the @foreach() statement.

<td>
<a href="#" 
data-pdsa-action="delete"
data-pdsa-arg="@item.ProductId"
data-pdsa-deletelabel="Product"
class="btn btn-default btn-sm">
<i class="glyphicon glyphicon-trash"></i>
</a>
</td>

Again, you are going to use the data-pdsa-arg attribute to hold the primary key of the product record to delete. There is also one additional data- attribute called data-pdsa-deletelabel. This allows you to pass in the text to display when the JavaScript confirm() function is called to ask the user if they wish to "Delete this Product?". The value Product is what is replaced in the string. Modify the pdsa-action.js file to display a "Delete" message to the user.

$(document).ready(function () {
  // Connect to any elements that have 'data-pdsa-action'
  $("[data-pdsa-action]").on("click", function (e) {
    var deletelabel = '';
    var submit = true;
    e.preventDefault();
    // Check for Delete
    if ($(this).data("pdsa-action") === "delete") {
      deletelabel = $(this).data("pdsa-deletelabel");
      if (!deletelabel) {
        deletelabel = 'Record';
      }
      if (!confirm("Delete this " + deletelabel + "?")) {
        submit = false;
      }
    }
    // Fill in hidden fields with action 
    // and argument to post back to model
    $("#EventAction").val($(this).data("pdsa-action"));
    $("#EventArgument").val($(this).data("pdsa-arg"));
    if (submit) {
      // Submit form with hidden values filled in
      $("form").submit();
    }
  });
});

Open the ProductViewModel class and add a new method to delete a product.

public virtual void Delete() {
  PTCData db = null;
  try {
    db = new PTCData();
    if (!string.IsNullOrEmpty(EventArgument)) {
      Entity = 
       db.Products.Find(Convert.ToInt32(EventArgument));
      db.Products.Remove(Entity);
      db.SaveChanges();
      PageMode = PDSAPageModeEnum.List;
    }
  }
  catch (Exception ex) {
    Publish(ex, "Error Deleting Product With ID=" 
                 + Entity.ProductName);
  }
}

Add a new case statement in the HandleRequest for deleting a product.

case "delete":
  Delete();
  break;

Run the Product page, click on a product, answer OK when prompted, and you should see the product table refreshed with the product you deleted no longer displayed.

Add Server-Side Validation

Now that you have all the client-side validation working, add similar functionality to the server-side code as well. As it is very simple for a hacker to bypass client-side validation, you always check to ensure the data is validated on the server-side as well. To do this you add a new class to the DataLayer project named PTCData-Extension. After the file is added, rename the class inside of the file to PTCData and make it a partial class. This will allow us to add additional functionality to the PTCData Entity Framework model created in part two of this blog post series.

public partial class PTCData
{
}

When you attempt to insert or update data using the Entity Framework, it first calls a method named ValidateEntity to perform the validation on any data annotations added to each property. You may override this method to add your own custom validations. Add the following code to the PTCData class in the PTCData-Extension.cs file you just added.

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) {
  return base.ValidateEntity(entityEntry, items);
}

Add a new method named ValidateProduct just after the ValidateEntity method you added. In this method is where you add your own custom validations. You return a list of DbValidationError objects for each validation that fails.

protected List<DbValidationError> ValidateProduct(Product entity) {
  List<DbValidationError> list = new List<DbValidationError>();
  return list;
}

The ValidateEntity method is called once for each entity class in your model that you are trying to validate. In our example, you are only validating the Product object since that is what the user is inputting. The entityEntry parameter passed into this method has an Entity property which contains the current entity being validated. Write code to check to see if that property is a Product object. If it is, pass that object to the ValidateProduct method. The ValidateProduct method returns a list of additional DbValidationError objects that need to be returned. If the list count is greater than zero, then return a new DbEntityValidationResult object by passing in the entityEntry property and your new list of DbValidationError objects.

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) {
List<DbValidationError> list = new List<DbValidationError>();
if (entityEntry.Entity is Product) {
Product entity = entityEntry.Entity as Product;
list = ValidateProduct(entity);
if (list.Count > 0) {
return new DbEntityValidationResult(entityEntry, list);
}
}
return base.ValidateEntity(entityEntry, items);
}

Now write the ValidateProduct method to perform the various validations for your Product data. Check the same validations you performed on the client-side.

protected List<DbValidationError> ValidateProduct(Product entity) {
  List<DbValidationError> list = new List<DbValidationError>();
  // Check ProductName field
  if (string.IsNullOrEmpty(entity.ProductName)) {
    list.Add(new DbValidationError("ProductName",
        "Product Name must be filled in."));
  }
  else {
    if (entity.ProductName.ToLower() ==
        entity.ProductName) {
      list.Add(new DbValidationError("ProductName",
        "Product Name must not be all lower case."));
    }
    if (entity.ProductName.Length < 4 ||
        entity.ProductName.Length > 150) {
      list.Add(new DbValidationError("ProductName",
        "Product Name must have between 4 and 150 characters."));
    }
  }
  // Check IntroductionDate field
  if (entity.IntroductionDate < DateTime.Now.AddYears(-2)) {
    list.Add(new DbValidationError("IntroductionDate",
      "Introduction date must be within the last two years."));
  }
  // Check Price field
  if (entity.Price < Convert.ToDecimal(0.1) ||
      entity.Price > Convert.ToDecimal(9999.99)) {
    list.Add(new DbValidationError("Price",
      "Price must be between $0.1 and less than $9, 999.99."));
  }
  // Check Url field
  if (string.IsNullOrEmpty(entity.Url)) {
    list.Add(new DbValidationError("Url",
        "Url must be filled in."));
  }
  else {
    if (entity.Url.Length < 5 ||
        entity.Url.Length > 255) {
      list.Add(new DbValidationError("Url",
        "Url must have between 5 and 255 characters."));
    }
  }
  return list;
}

Run the Product page and try putting in some values that are outside the ranges specified in this method and watch the messages displayed.

Summary

In this series of blog posts you learned techniques for using the Model-View-View-Model design pattern in the context of an MVC application. There are many advantages to using MVVM in an MVC application as you saw. By using these techniques, you should find yourself writing more reusable code and code that can be tested much easier.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Using MVVM in MVC Applications – Part 1

This blog post is the first in a series of four posts to discuss how to use a Model-View-View-Model (MVVM) approach in an MVC application. The MVVM approach has long been used in WPF applications, but has not been prevalent in MVC applications. Using a View Model class in MVC makes good sense as this blog post illustrates. You are going to be guided step-by-step building an MVC application using the Entity Framework and a View Model class to create a full CRUD web page.

Model-View-View-Model Approach

The reasons why programmers are adopting MVVM design pattern is the same reasons why programmers adopted Object Oriented Programming (OOP) over 30 years ago: reusability, maintainability and testability. Wrapping the logic of your application into classes allows you to reuse those classes in many different applications. Maintaining logic in classes allows you to fix any bugs in just one place and any other classes using that class automatically get the fix. When you don't use global variables in an application, testing your application becomes much simpler. Wrapping all variables and logic that operates upon those variables into one class allows you to create a set of tests to check each property and method in the class quickly and easily.

The thing to remember with MVVM is all you are doing is moving more of the logic out of the code behind of a user interface, or an MVC controller, and into a class that contains properties and methods you bind to the user interface. To use MVVM you must be using classes and not just writing all your code in a MVC controller. The whole key to MVVM or MVC is the use of classes with properties and methods that mimic the behavior you want in the UI. This means setting properties that are bound to UI controls and calling methods when you want to perform some action that you would normally write in a controller method.

Why Use MVVM in MVC

There are many reasons for using MVVM in an MVC application. Below are some of them.

1. Eliminate code in controller

a. Only two controller methods are needed

2. Simplify the controller logic

3. Can just unit test the view model and not the controller

4. View model classes can be reused in different types of projects (WPF, Web Forms, etc.)

Our Goal

This series of blog posts have a few goals for you to accomplish.

  1. Create a product table
  2. Create a set of MVC pages to list, search, add, edit, delete and validate product data
  3. Create a view model class to handle all these functions
  4. Write just two small methods in an MVC controller
  5. Learn ways to make more reusable and testable MVC applications

Create a Product Table

For this sample, create a table called Product in a SQL Server database. In the following code snippet, you see the full definition of the Product table.

CREATE TABLE Product (
  ProductId int NOT NULL 
                IDENTITY(1,1) 
                PRIMARY KEY NONCLUSTERED,
ProductName varchar(150) NOT NULL,
IntroductionDate datetime NOT NULL,
Url varchar(255) NOT NULL,
Price money NOT NULL
)

Add Data to the Table

Once you create the table, add some data to each field. Create enough rows that you can see several rows on your final HTML page. Below is the data that I used to create the list of product data for this sample application.


Figure 1: Product data

Here is the script to add the data to the Product table.

SET IDENTITY_INSERT Product ON 
GO
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (1, 'Extending Bootstrap with CSS, JavaScript and jQuery', '2015-06-11', 'http://bit.ly/1SNzc0i', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (2, 'Build your own Bootstrap Business Application Template in MVC', '2015-01-29', 'http://bit.ly/1I8ZqZg', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (3, 'Building Mobile Web Sites Using Web Forms, Bootstrap, and HTML5', '2014-08-28', 'http://bit.ly/1J2dcrj', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (4, 'How to Start and Run A Consulting Business', '2013-09-12', 'http://bit.ly/1L8kOwd', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (5, 'The Many Approaches to XML Processing in .NET Applications', '2013-07-22', 'http://bit.ly/1DBfUqd', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (6, 'WPF for the Business Programmer', '2009-06-12', 'http://bit.ly/1UF858z', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (7, 'WPF for the Visual Basic Programmer - Part 1', '2013-12-16', 'http://bit.ly/1uFxS7C', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (8, 'WPF for the Visual Basic Programmer - Part 2', '2014-02-18', 'http://bit.ly/1MjQ9NG', 29.0000);
SET IDENTITY_INSERT Product OFF
GO

Create All Projects

As there are three pieces to a MVVM application, the Model, the View and the View Model, there are three Visual Studio projects you need to build within a single solution.

  1. Data Layer Class Library (Model)
  2. MVC Web Project (View)
  3. View Model Class Library (View Model)

Let's start building those now.

Create the Web Project

Open Visual Studio and click on the File | New Project to display the New Project window. Select Web from the Templates under Visual C#. From the list in the middle of the window select ASP.NET Web Application (.NET Framework). Set the name of this project to PTC as shown in Figure 2.


Figure 2: Create a new MVC Application named PTC.

Click the OK button. When prompted, choose the MVC template as shown in Figure 3 and click the OK button.


Figure 3: Choose the MVC template

Add Entity Framework

You are going to be using the Entity Framework in all threes projects. While you are still building this MVC project, let's go ahead and add the necessary DLLs and configuration file entries. Right mouse click on the project and select Manage NuGet Packages… Click on the Browse tab and search for Entity Framework. Click the Install button as shown in Figure 4.


Figure 4: Install the Entity Framework using NuGet

Create the Data Layer Project

Let's now build a project to just hold all data access classes and any other Entity classes we need to create. Right mouse click on the solution and choose Add | New Project. Select Windows from the Templates on the left tree view. Select Class Library from the middle part of the window. Set the name to PTC.DataLayer as shown in Figure 5.


Figure 5: Create a Class Library project for your Data Layer

Click the OK button to create the new project. Delete the file Class1.cs as you won't need this. Right mouse click on this project and select Add | New Item… From the New Item window select Data | ADO.NET Entity Data Model. Set the name to PTCData as shown in Figure 6. Click the Add button.


Figure 6: Add an ADO.NET Entity Data Model to the Data Layer project

On the Entity Data Model Wizard screen that is now displayed (Figure 7), select the Code First from database option.


Figure 7: Choose the Code First from database option.

Click the Next button to advance to the next screen. On the next page of this wizard (Figure 8) add a connection string where you created the Product table. Leave everything else as it is on this page and click the Next button.


Figure 8: Add a connection string to point to your database.

You are now going to choose the Product table you created earlier in this blog post. Drill down into your collection of tables and check the Product table as shown in Figure 9.


Figure 9: Select the Product table

Click the Finish button and Visual Studio will generate new classes that allow you to create, read, update and delete data in the Product table.

Create View Model Project

The last project you need to add is one for your View Model classes. Right mouse click on the solution and choose Add | New Project. Select Windows from the Templates on the left tree view. Select Class Library from the middle part of the window. Set the name to PTC.ViewModel as shown in Figure 10.

Click the OK button. Rename Class1.cs to ProductViewModel.cs.


Figure 10: Add a Class Library project for your View Models

Add Entity Framework

Just like you added the Entity Framework to the MVC project, you need to add it to this project as well. Right mouse click on the project and select Manage NuGet Packages… Click on the Browse tab and search for Entity Framework. Click the Install button as shown in Figure 11.


Figure 11: Add the Entity Framework to your View Model layer

Add References

Now that you have all the projects created, you need to add the appropriate references in each. Add a reference to the PTC.DataLayer and PTC.ViewModelLayer project from the PTC project. Add a reference to the PTC.DataLayer project from the PTC.ViewModelLayer project.

Add Connection String to PTC Project

Copy the <connectionString> element from the PTC.DataLayer app.config file to the PTC web.config file.

<connectionStrings>
  <add name="PTCData" 
       connectionString="YOUR CONNECT STRING HERE"
       providerName="System.Data.SqlClient" />
</connectionStrings>

At this point, your solution should look like Figure 12.


Figure 12: Your solution is now ready to start working within.

Retrieve Product Data

Let's start to write code in the ProductViewModel to retrieve a collection of Products using the Entity Framework generated code. Open the ProductViewModel class and add a couple of using statements:

using PTC.DataLayer;
using System.Collections.Specialized;

Add a property to the view model class to hold the collection of products.

public List<Product> DataCollection { get; set; }

Add another property to hold any messages to display to the user.

public string Message { get; set; }

Add an Init() method to initialize the DataCollection property and message property.

public void Init() {     
  // Initialize properties in this class
  DataCollection = new List<Product>();
  Message = string.Empty;
}

Add a constructor to call the Init() method

public ProductViewModel()
  : base() {
  Init();
}

Add a couple of methods to handle exceptions. You are not going to do any exception publishing in this blog post, but you want to have the methods there so you can add it easily later.

public void Publish(Exception ex, string message) {
Publish(ex, message, null);
}
public void Publish(Exception ex, string message,
                    NameValueCollection nvc) {
// Update view model properties
Message = message;
// TODO: Publish exception here
}

Add a method named BuildCollection() that calls the PTCData class to retrieve the list of products from the database table.

protected void BuildCollection() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Get the collection
    DataCollection = db.Products.ToList();
  }
  catch (Exception ex) {
    Publish(ex, "Error while loading products.");
  }
}

HandleRequest Method

You are going to use the View Model class to handle many requests from your UI. Instead of exposing many different methods from the ProductViewModel class, let's create a single public method named HandleRequest(). For now, you are just going to call the BuildCollection() method from this method. However, later, you are going to add a switch…case statement to handle many different requests.

public void HandleRequest() {
  BuildCollection();
}

Create Product Controller

Now that you have the data layer and the view model classes created and ready to return data, you need a controller that can call our HandleRequest() method. Go back to the PTC project and right mouse click on the \Controllers folder. Select Add | Controller… from the menu. Choose the MVC 5 Controller – Empty template and click the Add method as shown in Figure 13.


Figure 13: Add an empty MVC contoller

When prompted to set the Controller name, type in ProductController and click the Add button as shown in Figure 14.


Figure 14: Create your Product controller

You will now see a controller class in your \Controllers folder that looks like the following.

public class ProductController : Controller
{
  // GET: Product
  public ActionResult Index() {
    return View();
  }
}

Add a using statement at the top of this class so you can use the ProductViewModel class in this controller.

using PTC.ViewModelLayer;

Modify the GET method to look like the following code.

public ActionResult Product() {
  ProductViewModel vm = new ProductViewModel();
  vm.HandleRequest();
  return View(vm);
}

List Products

Under the \Views folder see if you have a \Product folder already. If you don't, then add one. This folder is generally created when you add a controller called ProductController. Right mouse click on the \Views\Product folder and select the Add | MVC 5 View Page with Layout (Razor) from the menu as shown in Figure 15.


Figure 15: Add an MVC View with a shared layout page

When prompted to set the name for the item, type in Product as shown in Figure 16.


Figure 16: Create your Product MVC page

After setting the name you will be prompted to select the shared layout page. Select the \Views\Shared\_Layout.cshtml page as shown in Figure 17.


Figure 17: Select the _Layout.cshtml shared layout page

After the MVC page is added, add two statements as shown below.

@using PTC.ViewModelLayer
@model ProductViewModel

Write the code to display a table of product data. Add the following HTML after the code shown above.

<div class="table-responsive">
  <table class="table table-condensed table-bordered 
                table-striped table-hover">
    <thead>
      <tr>
        <th>Product Name</th>
        <th>Introduction Date</th>
        <th>Url</th>
        <th>Price</th>
      </tr>
    </thead>
    <tbody>
      @foreach (var item in Model.DataCollection) {
        <tr>
          <td>@item.ProductName</td>
          <td>
            @Convert.ToDateTime(item.IntroductionDate)
               .ToShortDateString()
          </td>
          <td>@item.Url</td>
          <td>
            @Convert.ToDecimal(item.Price).ToString("c")
          </td>
        </tr>
      }
    </tbody>
  </table>
</div>

Run the page and you should see a page that looks like the following.


Figure 18: You should now see some product data on your page

Summary

In this blog post, you created the start of an MVC application that is going to use a MVVM design pattern. You learned a few reasons why using an MVVM approach is a solid design decision in almost any kind of application. In the next blog post you learn to search for products and break up your single MVC page into a couple of different partial pages.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Using MVVM in MVC Applications – Part 2

This blog post continues from where the last blog post left off. You are going to learn to search for products. You also learn how to handle all post backs through a single method in your MVC controller. You will add code to check for no rows being returned, and display a message to the user. Finally you break up the single page into multiple partial pages.

Search for Products

You are now going to add a text box to allow the user to fill in a partial product name to search on. You are going to add two new buttons; one to allow the user to search on the product name they fill in, and one to clear the product name text box as shown in Figure 19.


Figure 19: Add a search area for your product page

To start out, you need a class to hold the search data. Currently there is only the one search field, Product Name, but you might add additional ones later. This means you only need to add one property to this new class.

Go to PTC.DataLayer project and add a new folder called \EntityClasses. Add a new class called ProductSearch under the \EntityClasses folder. Write the following code in this class.

public class ProductSearch
{
  public ProductSearch() : base() {
    Init();
  }
  public void Init() {
    // Initialize all search variables
    ProductName = string.Empty;
  }
  public string ProductName { get; set; }
}

Go to the PTC.ViewModelLayer project and open the ProductViewModel class. Add a using statement at the top of this class so you can use the new ProductSearch class.

using PTC.DataLayer.EntityClasses;

Add a new property to your view model class that you can use to bind to the user interface.

public ProductSearch SearchEntity { get; set; }

Add the following line of code to the Init() method

SearchEntity = new ProductSearch();

Locate the BuildCollection method and add the following code after you set the DataCollection to the results of the db.Products.ToList().

// Filter the collection
if (DataCollection != null && DataCollection.Count > 0) {
  if (!string.IsNullOrEmpty(SearchEntity.ProductName)) {
    DataCollection = DataCollection.FindAll(
      p => p.ProductName
        .StartsWith(SearchEntity.ProductName,
           StringComparison.InvariantCultureIgnoreCase));
  }
}

Hook up the Buttons

Many MVC developers add a separate controller method for each button and hyperlink they add to a page. This leads to a lot of methods in your controller. Instead, add a string property, named EventAction, in your ProductViewModel class that tells you which button or hyperlink was pressed. This string value is going to be set into the EventAction property via a tiny bit of jQuery code. Add the following property to the ProductViewModel class.

public string EventAction { get; set; }

Modify the Init() method to initialize this property.

EventAction = string.Empty;

Go back to the PTC web project, open the Product.cshtml page, and add a <form> tag around your HTML using the Html helper BeginForm() method.

@using (Html.BeginForm()) {
  // HTML Code you wrote before
}

Add a hidden control to bind to the EventAction property you created.

@using (Html.BeginForm()) {
  @Html.HiddenFor(m => m.EventAction, 
                  new { data_val = "false" })
  // HTML Code you wrote before
}

Build Search Input HTML

Add a panel to build the search area on the page. Add the following HTML below the hidden control and before the other HTML code you wrote earlier. Notice in this code you are binding to the ProductName property of the ProductSearch class you built earlier.

<div class="panel panel-primary">
  <div class="panel-heading">
    <h1 class="panel-title">Search for Products</h1>
  </div>
  <div class="panel-body">
    <div class="form-group">
      @Html.LabelFor(m => m.SearchEntity.ProductName,
                     "Product Name")
      @Html.TextBoxFor(m => m.SearchEntity.ProductName, 
                       new { @class = "form-control" })
    </div>
  </div>
  <div class="panel-footer">
    <button id="btnSearch"
            class="btn btn-sm btn-primary"
            data-pdsa-action="search">
      <i class="glyphicon glyphicon-search"></i>
      &nbsp;Search
    </button>
    <button id="btnReset"
            class="btn btn-sm btn-primary"
            data-pdsa-action="resetsearch">
      <i class="glyphicon glyphicon-share-alt"></i>
      &nbsp;Reset
    </button>
  </div>
</div>

Add JavaScript File for Retrieve Action

In the HTML you just wrote, notice the data-pdsa-action attributes are filled in with two string values; search and resetsearch. Each time one of the buttons is clicked on, you want to take the associated string value and put it into to the hidden field. You then submit the form to have it post back to the Product controller with the SearchEntity.ProductName value from the text box filled in, and the EventAction property filled in with either "search" or "resetsearch".

Right mouse click on the \scripts folder and select Add | JavaScript file. Set the name to pdsa-action.js. Write the following code within this file.

$(document).ready(function () {
  // Connect to any elements that have 'data-pdsa-action'
  $("[data-pdsa-action]").on("click", function (e) {
    e.preventDefault();
    // Fill in hidden field with action to post back to model
    $("#EventAction").val($(this).data("pdsa-action"));
    // Submit form with hidden values filled in
    $("form").submit();
  });
});

Go to the bottom of your Product.cshtml page and add a reference to this script file.

@section scripts {
  <script type="text/javascript"
          src="~/scripts/pdsa-action.js"></script>
}

Add Post Method in Controller

Now that you are ready to handle post backs from the user, you need to add an HttpPost method to your ProductController class. Open the ProductController class and add a new method that looks like the following.

[HttpPost]
public ActionResult Product(ProductViewModel vm) {
  // Handle action by user
  vm.HandleRequest();
  // Rebind controls
  ModelState.Clear();
      
  return View(vm);
}

Modify HandleRequest Method

Previously in the HandleRequest() method you had it calling the BuildCollection and nothing else. You now have a couple of new actions that can be performed. The user can choose to "search" or "resetsearch". You need to add a switch…case to handle these different event actions. Locate the HandleRequest() method in your ProductViewModel and modify it to look like the following.

public void HandleRequest() {
  // Make sure we have a valid event command
  EventAction = (EventAction == null ? "" :
                   EventAction.ToLower());
  switch (EventAction) {
    case "search":
      break;
    case "resetsearch":
      SearchEntity = new ProductSearch();
      break;
  }
  BuildCollection();
}

Run the application, type in "b" into the Product Name search box, click the Search button, and you should see just a list of products that start with the letter "b".

Inform User of No Rows

When the user searches and no rows are returned from that search, or if there are just no rows in the Product table, you should inform the user of this fact. You can display messages to the user using the Message property you added earlier to the ProductViewModel class. Modify the HandleRequest() method and add the following after the call to the BuildCollection() method.

if (DataCollection.Count == 0) {
  Message = "No Product Data Found.";
}

Open the Product.cshtml page and add the following lines of code around all the HTML that displays the table on this page. Don’t wrap it around the "search" area of the page.

@if (Model.DataCollection.Count > 0) {
  <div class="table-responsive">
  ...  // ALL THE OTHER HTML HERE
  </div>
}
else {
  <div class="row">
    <div class="col-xs-12">
      <div class="jumbotron">
        <h2>@Model.Message</h2>
      </div>
    </div>
  </div>
}

Run the Product page and enter a few random letters into the search text box. Click on the Search button and you should see the message displayed.

Use Partial Pages

Instead of putting all the HTML for this page on a single cshtml page, let’s break each area up into separate partial pages. Copy and paste the Product.cshtml page into the \Product folder. Rename the new file to _ProductList.cshtml. Leave the @model directive at the top of the file, and then just leave the logic for building the HTML table of product data.

Once again, copy and paste the Product.cshtml page into the \Product folder. Rename the new file to _ProductSearch.cshtml. Leave the @model directive at the top of the file, and leave everything that is associated with the search area. This is the HTML between the <div class="panel panel-primary"> and the </div> for that panel.

Open the Product.cshtml and modify this file to look like the following:

@using PTC.ViewModelLayer
@model ProductViewModel
@using (Html.BeginForm()) {
  @Html.HiddenFor(m => m.EventAction, 
                  new { data_val = "false" })
  @Html.Partial("_ProductSearch", Model)
  @Html.Partial("_ProductList", Model)
}
@section scripts {
  <script type="text/javascript" 
          src="~/scripts/pdsa-action.js"></script>
}

Run the application and everything should still be working.

Summary

In this blog post, you broke up the MVC page into two partial pages that are called from the main page. You learned how to handle all post backs with just a single post method in your MVC controller. You created additional code in your view model to handle searching for products. In the next blog post you add a detail page and learn to add products to the product table.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Using MVVM in MVC Applications – Part 3

This blog post continues from the last two blog posts that you can find here.

In this post you add a product detail page in order to gather product data for adding to the product table. You add a save and a cancel button and learn to display validation messages. You build a method in the view model class to insert product data.

Add a Product

To add or update a product, you need to create a detail page (Figure 21) with data entry fields for each field in the table. You need to be able to display validation errors if the user does not fill out correct data for the fields. You also need to hide the Search and List areas of the screen while the user is in add or edit mode.

Create an enumeration that can help you keep track of what "mode" the page is in. There are three different modes; List, Add, and Edit. So, you need three enumerated values. Add a file in your PTC.ViewModelLayer project called PDSAPageModeEnum.cs. Remove any code in this file and replace it with the following enum definition.

public enum PDSAPageModeEnum
{
List,
Add,
Edit
}

Open the ProductViewModel class and add a new property called PageMode that is of the type of this enumeration. Also, add a property called Entity, of the type Product, that you can use to bind to the fields on the detail page. One more property you need is a boolean called IsValid. This property informs the page to display any validation errors.

public PDSAPageModeEnum PageMode { get; set; }
public Product Entity { get; set; }
public bool IsValid { get; set; }

Modify the Init() method to initialize these three new properties.

PageMode = PDSAPageModeEnum.List;
Entity = new Product();
IsValid = true;

Modify the HandleRequest method and add two new case statements. You also need to set the PageMode to List within the other case statements.

public void HandleRequest() {
  // Make sure we have a valid event command
  EventAction = (EventAction == null ? "" :
                 EventAction.ToLower());
  Message = string.Empty;
  switch (EventAction) {
    case "add":
      IsValid = true;
      PageMode = PDSAPageModeEnum.Add;
      break;
    case "edit":
      IsValid = true;
      PageMode = PDSAPageModeEnum.Edit;
      break;
    case "search":
      PageMode = PDSAPageModeEnum.List;
      break;
    case "resetsearch":
      PageMode = PDSAPageModeEnum.List;
      SearchEntity = new ProductSearch();
      break;
  }
  if (PageMode == PDSAPageModeEnum.List) {
    BuildCollection();
    if (DataCollection.Count == 0) {
      Message = "No Product Data Found.";
    }
  }
}

You need a way for the user to go into Add or Edit mode. Start with the Add mode by adding an Add button to the search panel as shown in Figure 20.


Figure 20: An Add button allows you to navigate to a blank detail page

Open the _ProductSearch.cshtml file and locate the Reset button. Just below this button add an addition button for the Add.

<button id="btnAdd"
        class="btn btn-sm btn-primary"
        data-pdsa-action="add">
  <i class="glyphicon glyphicon-plus"></i>
  &nbsp;Add
</button>

With just this line of code above, you can run the application and if you were to set a breakpoint on the line in the HandleRequest method that sets the PageMode = PDSAPageModeEnum.Add; you would see that you stop on that line. This is a nice feature of using the data-pdsa-action attribute, you simply add a new data-pdsa-action="some value" and the HandleRequest method is called with the appropriate value set in the EventAction. You just need to add additional case statements to handle the new actions you wish to work with.

Build the Detail Page

It is now time to build the Product data entry page. Add a new partial page in the \Product folder named _ProductDetail.cshtml. Add the following HTML to create the page shown in Figure 21.

@model PTC.ViewModelLayer.ProductViewModel
<div class="panel panel-primary">
  <div class="panel-heading">
    <h1 class="panel-title">
      Product Information
    </h1>
  </div>
  <div class="panel-body">
    <!-- ** BEGIN MESSAGE AREA -->
    <div class="row">
      <div class="col-xs-12">
        @if (!Model.IsValid) {
          <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>
            <!-- Model State Errors -->
            @Html.ValidationSummary(false)
          </div>
        }
      </div>
    </div>
    <!-- ** END MESSAGE AREA -->
    <!-- ** BEGIN INPUT AREA -->
    @Html.HiddenFor(m => m.Entity.ProductId)
    <div class="form-group">
      @Html.LabelFor(m => m.Entity.ProductName,
                 "Product Name")
      @Html.TextBoxFor(m => m.Entity.ProductName,
           new { @class = "form-control" })
    </div>
    <div class="form-group">
      @Html.LabelFor(m =>
       m.Entity.IntroductionDate,
       "Introduction Date")
      @Html.TextBoxFor(m =>
       m.Entity.IntroductionDate,
       new { @class = "form-control" })
    </div>
    <div class="form-group">
      @Html.LabelFor(m => m.Entity.Url, "Url")
      @Html.TextBoxFor(m => m.Entity.Url,
       new { @class = "form-control" })
    </div>
    <div class="form-group">
      @Html.LabelFor(m => m.Entity.Price, "Price")
      @Html.TextBoxFor(m => m.Entity.Price,
       new { @class = "form-control" })
    </div>
    <!-- ** END INPUT AREA -->
  </div>
  <div class="panel-footer">
    <div class="row">
      <div class="col-sm-12">
        <button id="btnSave"
                class="btn btn-sm btn-primary"
                data-pdsa-action="save">
          <i class="glyphicon
           glyphicon-floppy-disk"></i>
          &nbsp;Save
        </button>
        <button id="btnCancel"
                class="btn btn-sm btn-primary"
                formnovalidate="formnovalidate"
                data-pdsa-action="cancel">
          <i class="glyphicon
           glyphicon-remove-circle"></i>
          &nbsp;Cancel
        </button>
      </div>
    </div>
  </div>
</div>

Open the Product.cshtml page and modify the code in the @using (Html.BeginForm()) area. You are going to add a new hidden field to hold the current state of the PageMode property. You are also going to wrap an if statement around the rendering of the partial pages. You want to display the pages based on the mode of the view model.

@using (Html.BeginForm()) {
  @Html.HiddenFor(m => m.EventAction, 
                  new { data_val = "false" })
  @Html.HiddenFor(m => m.PageMode, 
                  new { data_val = "false" })
  if (Model.PageMode == PDSAPageModeEnum.List)
  {
    @Html.Partial("_ProductSearch", Model)
    @Html.Partial("_ProductList", Model)
  }
  if (Model.PageMode == PDSAPageModeEnum.Add ||
      Model.PageMode == PDSAPageModeEnum.Edit) {
    @Html.Partial("_ProductDetail", Model)
  }
}

You should be able to run the page, click on the Add button and you see the product detail page appear. You can’t click on the Save or Cancel buttons as you have not programmed those yet.


Figure 21: The detail page for inserting or updating a product

Cancel Button

First create the logic in your view model class to handle the user pressing the Cancel button on the detail page. Open the ProductViewModel class and add a new case statement as shown below.

case "cancel":
  PageMode = PDSAPageModeEnum.List;
  break;

Just these three lines of code will cause the page to go back into List mode, rebuild the collection of products and display the HTML table of products. Run the application, click on the Add button, then click on the Cancel button, and you should be able to cancel out of the detail screen.

Save Button

When the user clicks on the Save button, you are going to either Insert or Update the product data depending on how the user navigated to the detail page. You have only created the Add button so far, but later you will build the edit functionality. To prepare for both inserting and updating of product data, add some method stubs to your ProductViewModel class as shown below.

protected void Insert() {
}
protected void Update() {
}
protected void Save() {
  IsValid = true;
  if (PageMode == PDSAPageModeEnum.Add) {
    Insert();
  }
  else {
    Update();
  }
}

Locate the HandleRequest() method and add a new case statement to handle the "save" event action.

case "save":
  Save();
  break;

Validation Messages

When you attempt to insert or update, the user may not put in good information and thus some validation rules may fail. You need to report those errors back to the user. Earlier you added the @Html.ValidationSummary() method to generate any validation rule failures from Data Annotations generated by the Entity Framework. However, these will only display if JavaScript is turned on in the user’s browser. And, of course, hackers can bypass these rules easily.

You also need to test business rules on the server-side in case things are bypassed, and because we can write more rules that are not able to be added as Data Annotations at the time of generation. To handle these situations, you are going to add a property to your ProductViewModel class to hold a collection of string messages. This collection can be displayed on the _ProductDetail.cshtml page. 

At the top of the ProductViewModel class add a new using statement. This statement is needed as you are going to work with some objects from the Entity Framework namespace.

using System.Data.Entity.Validation;

Create a property named Messages to hold a collection of messages to display to the user.

public List<string> Messages { get; set; }

Modify the Init() method to initialize this new collection.

Messages = new List<string>();

Add a method to add the failures contained in a DbValidationException object to the Messages collection. This exception object is thrown by the Entity Framework if any validation rules fail. You will learn how a little later. For now, write the following method.

protected void ValidationErrorsToMessages(DbEntityValidationException ex) {
  foreach (DbEntityValidationResult result in ex.EntityValidationErrors) {
    foreach (DbValidationError item in result.ValidationErrors) {
      Messages.Add(item.ErrorMessage);
    }
  }
}

To display these messages, or any single message, open the _ProductDetail.cshtml page and right below the @Html.ValidationSummary(false) line add the following code.

@if (Model.Messages.Count > 0) {
<ul>
@foreach (string item in Model.Messages) {
<li>@item</li>
}
</ul>
}
else {
<span>@Html.Raw(Model.Message)</span>
}

Insert Method

It is now time to write the code to perform the insert into the Product table. Locate the Insert() method in your ProductViewModel class and add the following code.

protected void Insert() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Do editing here
    db.Products.Add(Entity);
    db.SaveChanges();
    PageMode = PDSAPageModeEnum.List;
  }
  catch (DbEntityValidationException ex) {
    IsValid = false;
    ValidationErrorsToMessages(ex);
  }
  catch (Exception ex) {
    Publish(ex, "Error Inserting New Product: '"
                + Entity.ProductName + "'");
  }
}

When the SaveChanges() method is executed, validation errors could occur here. This is when the catch block that accepts a DbEntityValidationException would execute. You then take that exception object and pass it to the ValidationErrorsToMessages method to extract the message and put it into the Messages property. These messages would then be displayed in the message area of the detail page.

Modify the Post Method in the Product Controller

Now that you are going to handle any validation errors, you want to first check to see if the ModelState property is valid. This value is automatically checked by MVC for any Data Annotations that generated validation rules to be checked. Modify the POST method in the ProductController to look like the following.

[HttpPost]
public ActionResult Product(ProductViewModel vm) {
  vm.IsValid = ModelState.IsValid;
  if (ModelState.IsValid) {
    // Handle action by user
    vm.HandleRequest();
    // Rebind controls
    ModelState.Clear();
  }
  return View(vm);
}

Run the application and click on the Add button. Immediately click on the Save button without filling any information into the page, and you should see an error message appear. Now fill in some good product information and press the Save button. You should see the new information show up in the list.

Summary

In this post, you added a product detail page to gather product data from the user. You learned how to display validation messages if the data was not input correctly. In addition, you added an insert method to the view model class in order to add data to the product table. In the next post you write code to select a single product from the product table. You also learn to update and delete product data.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Add Angular to MVC – Part 4

If you have not already done so, you should go back and read the last three blog posts on how to integrate Angular 2 into an MVC application.

In this blog post, you will learn to retrieve a single product record using a Web API call from the Angular product service you created. You are going to add Edit and Delete buttons to each row of the HTML table (Figure 1) to allow the user to update and delete an existing product record. For this post, I am assuming you are a Microsoft Visual Studio developer and are familiar with MVC, Angular, C#, and the Web API.

Open up the project you created in the last blog post and follow along with the steps outlined in this one to create the final project.


Figure 1: Edit and delete buttons are added to each row in your product table

Get a Single Product

When the user clicks on the Edit button next to a specific product in the HTML table, you should go back to the server to retrieve the full product record for editing. This ensures you are getting the latest product data.

Add Get to Controller

To get a single product add a Get() method to your ProductApiController class. This Get() method is different from the other one in this class in that it accepts a product id of the product you wish to retrieve. Add this method, shown below to your ProductApiController class.

[HttpGet]
public IHttpActionResult Get(int id) {
  IHttpActionResult ret;
  ProductViewModel vm = new ProductViewModel();
  vm.GetProduct(id);
  if (vm.Entity != null) {
    ret = Ok(vm.Entity);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add Get to Angular Product Service

Now that you have the Web API method created, write a getProduct() method in the ProductService component. Open the product.service.ts file and add the following method.

getProduct(id: number): Observable<Product> {
  let url = this.url + "/" + id;
  return this.http.get(url)
    .map(response => response.json() as Product)
    .catch(this.handleErrors);
}

This method builds a URL that looks like the following: api/productApi/2. The number 2 on the end is what gets passed to the id parameter in the Get() method in your ProductApiController.

Add Select Button to HTML Table

As you saw in Figure 1, you need to add an “Edit” column to your table. Open the product-list.component.html file and insert a new <td> element within the <thead> element.

<td>Edit</td>

Move down to the <tbody> element and insert a new <td> element in the same position.

<td>
  <button class="btn btn-default btn-sm"
          (click)="selectProduct(product.productId)">
    <i class="glyphicon glyphicon-edit"></i>
  </button>
</td>

Modify Product List Component

The click event on this button is going to call a method named selectProduct(). You pass in the product id to this method in order to pass this id to the detail page so it can load the product data associated with that id. Add the selectProduct() function to the ProductListComponent. This function is going to call the navigate function and pass that id to the product detail page.

selectProduct(id: number) {
  this.router.navigate(['/productDetail', id]);
}

Retrieve a Passed Parameter

You are going to need to modify the ngOnInit() method you previously wrote in the ProductDetailComponent. When you created the add functionality in the last blog post, you did not do anything with the parameter that was passed into the ProductDetailComponent. Now, since you are passing an id, you are going to use that id to call the getProduct method on the product service to retrieve the product. Open the product-detail.component.ts file and modify the ngOnInit() function to look like the following.

ngOnInit() {
  this.route.params.forEach((params: Params) => {
    if (params['id'] !== undefined) {
      if (params['id'] != "-1") {
        this.productService.getProduct(params['id'])
          .subscribe(product => this.product = product,
              errors => this.handleErrors(errors));
      }
      else {
        this.product = new Product();
        this.product.price = 1;
        this.product.url = "http://www.fairwaytech.com";
      }
    }
  });
}

In the above code you loop through the route.params array and retrieve a Params object. You check to see if the ‘id’ parameter is defined on that Params object. If the id value exists, check if that value to see if it is equal to a -1. If so, then assume you are adding a product. If the value is anything else, then it is a valid product id. Call the getProduct() method on the product service to retrieve a single product object.

At this point you can run the application and click on the Edit button. If you did everything correctly, you should see product data in all the input fields.

Update a Product

Now that you have the current product data displayed in the input fields, the user may update them. Create the appropriate code to perform the updating now.

Add PUT Method in Controller

Our first step, of course, is to add a new PUT method in the ProductApiController class. Open the ProductApiController.cs file and add the following code.

[HttpPut()]
public IHttpActionResult Put(int id, Product product) {
  IHttpActionResult ret = null;
  ProductViewModel vm = new ProductViewModel();
  if (product != null) {
    if (vm.Update(product)) {
      ret = Ok(vm.Entity);
    }
    else {
      if (vm.Messages.Count > 0) {
        ret = BadRequest(
               ConvertMessagesToModelState(vm.Messages));
      }
      else if (vm.LastException != null) {
        ret = InternalServerError(vm.LastException);
      }
    }
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add updateProduct to Product Service

Add another method to your Angular product service class to call this new PUT method. Open the product.service.ts file and add the following updateProduct() method.

updateProduct(product: Product): Observable<Product> {
  let headers = new Headers({ 'Content-Type': 
                              'application/json' });
  let options = new RequestOptions({ headers: headers });
  return this.http.put(this.url + "/" + product.productId,
                        product, options)
    .map(this.extractData)
    .catch(this.handleErrors);
}

When you PUT data, as opposed to getting data, you need to specify the content type as JSON data. You do this by creating a new Headers object and setting the ‘Content-Type’ property to ‘application/json’. Create a RequestOptions object and set the headers property to this new Headers object you created. Call the put method on the Http service passing in the product object and the RequestOptions object. When calling a PUT you specify the URL and you also add on the product id to that URL.

Modify the updateProduct() Method

In the last blog post you wrote an updateProduct method that was empty. It is now time to fill in this method. Open the product-detail.component.ts file and modify the updateProduct method to call the updateProduct method you created in the product service.

updateProduct(product: Product) {
  this.productService.updateProduct(product)
    .subscribe(() => this.goBack(),
      errors => this.handleErrors(errors));
}

Delete a Product

The last piece of functionality to add to your product page is the ability to delete product.

Add DELETE to Controller

Add a DELETE method to your ProductApiController to which you pass in the product id to delete. Open the ProductApiController.cs file and add the Delete method shown below.

[HttpDelete]
public IHttpActionResult Delete(int id) {
  IHttpActionResult ret;
  ProductViewModel vm = new ProductViewModel();
  if (vm.Delete(id)) {
    ret = Ok(vm.Entity);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add deleteProduct to ProductService

Add a method to your Angular product service to call the delete method in your Web API. Open the product.service.ts file and add the deleteProduct() method shown below.

deleteProduct(id: number): Observable<Product> {
  return this.http.delete(this.url + "/" + id)
    .map(() => null)
    .catch(this.handleErrors);
}

Add Delete Button to HTML Table

As you saw in Figure 1, you need to add a “Delete” column to your table. Open the product-list.component.html file and insert a new <td> element within the <thead> element. Make this the last element in the <thead> element.

<td>Delete</td>

Add a <td> as the very last element in the <tbody> tag. Add a button with a click event that calls a method in your ProductListComponent class. Pass the current product id in the table to this method.

<td>
  <button class="btn btn-default btn-sm"
          (click)="deleteProduct(product.productId)">
    <i class="glyphicon glyphicon-trash"></i>
  </button>
</td>

Add deleteProduct() Method in List Component

Now that you have a button to call a deleteProduct method, go ahead and add that method. Open the product-list.component.ts file and add the code shown below.

deleteProduct(id: number) {
  if (confirm("Delete this product?")) {
    this.productService.deleteProduct(id)
      .subscribe(() => this.getProducts(),
      errors => this.handleErrors(errors));
  }
}

This method first confirms with the user that they really wish to delete this product. If they respond affirmatively, the deleteProduct() method on the Angular product service is called. If the delete is successful, the getProducts method is called to refresh the collection of products from the server and redisplay the list of products.

Summary

In this blog post you finished your product page by learning to retrieve, update and delete an existing product record. In these four blog posts you learned how to replace a single MVC page with an Angular page. There is no need to completely rewrite an MVC application. Instead, you can just replace a few pages that might need the performance of Angular.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Angular to MVC – Part 4. NOTE: After downloading the sample, you will need to right mouse click on the package.json file and select the menu “Restore Packages”. You also need to create the Product table in a SQL Server database and update the connection string to point to your server and database name.


Add Angular to MVC – Part 3

If you have not already done so, you should go back and read the last two blog posts on how to integrate Angular 2 into an MVC application. 

In this blog post, you are going to extend the last sample to allow the user to add a new product. You are going to add a POST method to your Web API controller. You will also create a new Angular component to handle getting and displaying a product record.

Add an Add Button to Product List Page

You need to have a way to get into an “add” mode where you allow a user to enter all the fields to create a new product as shown in Figure 1. You will create the appropriate HTML for this page soon, but first, let’s create an Add button on the main product listing page to get to this page.


Figure 1: Add new products via a product detail page.

Open the product-list.component.html file, and just below the <h2> tag add the following HTML.

<div class="row">
  <div class="col-xs-12">
    <button class="btn btn-primary" 
            (click)="add()">
      Add New Product
    </button>
  </div>
</div>

When you click on the Add New Product button, you want to route to the new product detail page you are going to create. The add() function that is going to be called from the Click event on this button will perform this routing functionality. 

Update Product List Component

Let’s update the ProductListComponent class to perform this add functionality. Open the product-list.component.ts file and add an import to use the routing service in your class.

import { Router } from '@angular/router';

Locate the constructor in your ProductListComponent and add a second parameter to this constructor to accept the Router service.

constructor(private productService: ProductService,
  private router: Router) {
}

Next, add a new function called add(). This function uses the injected router service to call the navigate function. You pass an array to this navigate function. The first parameter is a route to match up with a route you create in your routing component. The second parameter is any parameter you wish to pass. Later you use this product detail page to display an existing product, so you will pass a real product ID as the second parameter. For now, since you are just adding a new product, pass a -1.

add() {
  this.router.navigate(['/productDetail', -1]);
}

Create Detail HTML

Let’s now add the detail page to accept product data from the user. Right mouse click on the \app\product folder and select Add | HTML page. Set the name to product-detail.component.html and click the OK button. Delete all the HTML in the new page and add the following HTML.

<div class="row" *ngIf="product">
  <div class="col-xs-12">
    <div class="panel panel-primary">
      <div class="panel-heading">
        <h1 class="panel-title">Product Information</h1>
      </div>
      <div class="panel-body">
        <!-- ** BEGIN: Error Message Area ** -->
        <div class="row" 
             *ngIf="messages && messages.length">
          <div class="col-xs-12">
            <div class="alert alert-warning">
              <ul>
                <li *ngFor="let msg of messages">
                  {{msg}}
                </li>
              </ul>
            </div>
          </div>
        </div>
        <!-- ** END: Error Message Area ** -->
        <!-- ** BEGIN: Detail Entry Area ** -->
        <div class="form-group">
          <label for="productName">Product Name</label>
          <input id="productName"
                 type="text"
                 class="form-control"
                 autofocus="autofocus"
                 placeholder="Enter the Product Name"
                 title="Enter the Product Name"
                 [(ngModel)]="product.productName" />
        </div>
        <div class="form-group">
          <label for="introductionDate">
            Introduction Date</label>
          <input id="introductionDate"
                 type="text"
                 class="form-control"
                 placeholder="Enter the Introduction Date"
                 title="Enter the Introduction Date"
                 [(ngModel)]="product.introductionDate" />
        </div>
        <div class="form-group">
          <label for="price">Price</label>
          <input id="price"
                 type="number"
                 class="form-control"
                 placeholder="Enter the Price"
                 title="Enter the Price"
                 [(ngModel)]="product.price" />
        </div>
        <div class="form-group">
          <label for="url">URL</label>
          <input id="url"
                 type="url"
                 class="form-control"
                 placeholder="Enter the URL"
                 title="Enter the URL"
                 [(ngModel)]="product.url" />
        </div>
        <!-- ** END: Detail Entry Area ** -->
      </div>
      <div class="panel-footer">
        <button class="btn btn-success"
                (click)="saveProduct()">Save</button>
        <button class="btn btn-primary"
                (click)="goBack()">Cancel</button>
      </div>
    </div>
  </div>
</div>

Listing 1: The HTML for the product detail page.

NOTE: I have purposefully left all validation off each input field. You will learn about validation later.

Create Product Detail Component

Now that you have a product detail page, you need a component to go along with it. Right mouse click on the \app\product folder and select Add | TypeScript file. Set the name to product-detail.component.ts. Add the following code for now. You will add more to this component later.

import { Component, OnInit } from "@angular/core";
import { Product } from "./product";
@Component({
  moduleId: module.id,
  templateUrl: "./product-detail.component.html"
})
export class ProductDetailComponent implements OnInit {
  product: Product;
  messages: string[] = [];
  ngOnInit() {
    this.product = new Product();
    this.product.price = 1;
    this.product.url = "http://www.fairwaytech.com";
  }
}

Update Routing

You have added the new detail component and HTML. You also wrote code in the ProductListComponent to navigate to this new detail component. However, before you can do that, you need to inform the Angular routing service about the new detail component. Open the app-routing.module.ts file and add a new import statement at the top of this file.

import { ProductDetailComponent } from "./product/product-detail.component";

Add a new route object after the other routes you had previously created. This new route object references the ProductDetailComponent. The path property is a little different because you want to pass a parameter name id to the ProductDetailComponent class. For the add functionality, you are not going to do anything with this -1 parameter you are passing in, however, for editing, you will pass in a valid product id value in order to retrieve the product record to edit.

const routes: Routes = [
  {
    path: 'productList',
    component: ProductListComponent
  },
  {
    path: 'Product/Product',
    redirectTo: 'productList'
  },
  {
    path: 'productDetail/:id',
    component: ProductDetailComponent
  }
];

Update AppModule

In the product detail HTML you reference the ngModel directive. However, you have not told your Angular application that you wish to use this directive. In order to do this, open the app.module.ts file and add an import statement for the FormsModule package. This package includes the ngModel directive.

import { FormsModule } from '@angular/forms';

While you are in this file, also add an import for your new ProductDetailComponent class you added.

import { ProductDetailComponent } from "./product/product-detail.component";

The FormsModule needs to be added to the imports property on your NgModule decorator. The ProductDetailComponent should be added to the declarations property on your NgModule decorator. Modify the NgModule decorator to look like the following code.

@NgModule({
imports: [BrowserModule, AppRoutingModule, 
           HttpModule, FormsModule],
declarations: [AppComponent, 
                ProductListComponent, 
                ProductDetailComponent],
bootstrap: [AppComponent],
providers: [ProductService]
})

Run the application, click on the Add New Product button and you should see the detail page appear. Nothing else works at this point, but you just want to verify that you can get to this point.

Add POST Method in Controller

To add the data the user inputs into the product detail page you just created, you are going to need a POST method in your ProductApiController. When you attempt to add a new product, business rules could fail because the user did not fill out the fields correctly. For instance, a product name is required. If the user does not fill one out and the ProductName property gets passed to the server as a blank string, the code generated by the Entity Framework will raise an exception.

The Insert() method in the ProductViewModel handles that situation and converts all the validation errors generated by the Entity Framework into a KeyValuePair<string, string> object. The key property in the KeyValuePair object is the name of the property that was in error. The value property in the KeyValuePair object is the error message supplied by the Entity Framework. When you call the Insert() method in the ProductViewModel, the Messages property might be filled in with a set of error messages. If this is so, you need to take those messages and pass those back from the Web API.

The mechanism you use to communicate back to the caller that some validation failed on an POST or PUT is to pass back a 400 (BadRequest) and place a ModelStateDictionary object with the set of validation errors as the payload. This means you need to take the list of KeyValuePair objects from the view model and create a ModelStateDictionary object. Open the ProductApiController.cs file and add the following using statement.

using System.Web.Http.ModelBinding;

One note, on the above using statement. The ModelStateDictionary used by the Web API is different from the one used by MVC controllers. Make sure you are using the ModelStateDictionary class from the above namespace and not the one used by MVC.

Now, you can write the ConvertMessagesToModelState method as shown below.

private ModelStateDictionary ConvertMessagesToModelState(
      List<KeyValuePair<string, string>> messages) {
  ModelStateDictionary ret = new ModelStateDictionary();
  foreach (KeyValuePair<string, string> msg in messages) {
    ret.AddModelError(msg.Key, msg.Value);
  }
  return ret;
}

Before writing the Post() method, add a using statement so you can use the Product class from the Entity Framework.

using PTC.Models;

Write the POST method to accept a Product object from the Angular caller. This Post() method simply passes this Product object to the ProductViewModel for processing. You then check the state of the view model to determine what you should return as the IHttpActionResult.

[HttpPost]
public IHttpActionResult Post(Product product) {
  IHttpActionResult ret = null;
  ProductViewModel vm = new ProductViewModel();
  if (product != null) {
    if (vm.Insert(product)) {
      ret = Created<Product>(
            Request.RequestUri +
            vm.Entity.ProductId.ToString(),
              vm.Entity);
    }
    else {
      if (vm.Messages.Count > 0) {
        ret = BadRequest(
          ConvertMessagesToModelState(vm.Messages));
      }
      else if (vm.LastException != null) {
        ret = InternalServerError(vm.LastException);
      }
    }
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Create addProduct Method in Product Service

Now that you have a Web API that can be sent new product data to, let’s write code in the ProductService class you created in the earlier blog posts. Open the product.service.ts file and import two new services; Headers and RequestOptions

import { Http, Response, Headers, RequestOptions } from '@angular/http';

Next, add an addProduct() function to this class to post a new product object to the POST method on your ProductApiController class.

addProduct(product: Product): Observable<Product> {
  let headers = new Headers({ 'Content-Type': 'application/json' });
  let options = new RequestOptions({ headers: headers });
  return this.http.post(this.url, product, options)
    .map(this.extractData)
    .catch(this.handleErrors);
}

When you post data, as opposed to getting data, you need to specify the content type as JSON data. You do this by creating a new Headers object and setting the ‘Content-Type’ property to ‘application/json’. Create a RequestOptions object and set the headers property to this new Headers object you created. Next, call the post method on the Http service passing in the product object and the RequestOptions object.

Check for Validation Errors

One of three things could happen when you call the Post method. The data will be successfully added to the back-end database table. A set of validation errors is returned via a 400 error. Or, you could get an exception, in which case, a 500 error is sent back. When you wrote the handleErrors() function before, you handled a 404 and a 500 error, but you did not account for a 400. Add a new case statement to handle a 400 in the handleErrors() function.

private handleErrors(error: any): Observable<any> {
  let errors: string[] = [];
  switch (error.status)
  {
    case 400:  // Model State Error
      let valErrors = error.json().modelState;
      for (var key in valErrors)
      {
        for (var i = 0; i < valErrors[key].length; i++) {
          errors.push(valErrors[key][i]);
        }
      }
      break;
   ...
   return Observable.throw(errors);
}

In this new case statement, you retrieve the modelState property and loop through all the key values and retrieve the message from the properties returned. Each of these messages is pushed onto the errors array which is then sent back to the caller via the Observable.throw() function.

Modify Product Detail Component

Now that you have the server-side Web API written, and you created the product service on the client-side to call that, it is not time to add the appropriate code to the ProductDetailComponent class you created earlier to call the product service. First, add three new imports at the top of the product-detail.component.ts file.

import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
import { ProductService } from "./product.service";

Add a constructor to this class.

constructor(
  private productService: ProductService,
  private route: ActivatedRoute,
  private location: Location
) { }

Add a method to allow the user to go back to the previous page if they click on the cancel button.

goBack(){
  this.location.back();
}

Add a method to your class named handleErrors(). This method is called if the call to the addProduct in the Product Service fails. In this method you loop through the string array of errors and add them to the messages property.

private handleErrors(errors: any) {
  for (let msg of errors) {
    this.messages.push(msg);
  }
}

Even though you are only performing an add in this blog post, go ahead and add a stub function for updating a product as well. Create three new methods; updateProduct, addProduct and saveProduct().

private updateProduct(product: Product) {
}
private addProduct(product: Product) {
  this.productService.addProduct(product)
    .subscribe(() => this.goBack(),
      errors => this.handleErrors(errors));
}
saveProduct() {
  if (this.product) {
    if (this.product.productId) {
      this.updateProduct(this.product);
    }
    else {
      this.addProduct(this.product);
    }
  }
}

See the Validation Errors

Run the application, click on the Add New Product button. Immediately click on the Save button and you should see a set of validation errors appear on the screen as shown in Figure 2.


Figure 2: Validation errors show up at the top of your detail page.

Add a New Product

Now, go ahead and add some good data for the product, click the Save button and you should be redirected back to the list page where you will see the new product you just added within the list.

Summary

In this blog post you created a detail page to enter new product data. You added a new route to this page and created a component to handle the processing of the new product data. You also added a POST method to your Web API controller. You then created a function in your Angular product service to post the data to the Web API. You also saw how to handle validation errors returned from the server.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Angular to MVC – Part 3. NOTE: After downloading the sample, you will need to right mouse click on the package.json file and select the menu “Restore Packages”. Create the product by locating the \SqlScripts\Product.sql file and run the script in a SQL Server database. Open the Web.config file in the project and update the connection string to point to your server and database name.

Add Angular to MVC – Part 2

In the first part of this blog series, you added Angular 2 to an MVC application using Visual Studio. In this blog post, you will learn how to add a Web API that can be called from an Angular service. You will modify the Global.asax to automatically convert pascal-cased properties in C# classes into camel-cased TypeScript properties. You will build an Angular service, learn to inject it into a component, then call the service to retrieve product data. Finally, you will take the returned data and build an HTML table. For this post, I am assuming you are a Microsoft Visual Studio developer and are familiar with MVC, Angular, C#, and the Web API.


Figure 1: The product list page written using Angular

Open up the project you created in the last blog post and follow along with the steps outlined in this one to create the final project. 

Create Web API

To retrieve data from an Angular application, or any client-side technology, you create a Web API. Right mouse click on the \Controllers folder and select Add | Web API Controller Class (v2.1) from the menu. Set the name to ProductApiController and click the OK button.

Delete all the code within this new controller class as you are going to write your Web API methods using a more updated approach than what is generated by Visual Studio. Type in the code listed in the code snippet below to create a method that retrieves all products from the mock data returned from the ProductViewModel class.

public IHttpActionResult Get() {
  IHttpActionResult ret;
  ProductViewModel vm = new ProductViewModel();
  vm.LoadProducts();
  if (vm.Products.Count() > 0) {
    ret = Ok(vm.Products);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add WebApiConfig Class

If you did not have the Web API in your MVC application before, then you need to add a WebApiConfig class to your project. Look in the the \App_Start folder and see if the WebApiConfig.cs class exists. If not, right mouse click on the \App_Start folder and add a new class. Set the name to WebApiConfig and click the OK button. Make the class file look like the following:

using System.Web.Http;
namespace PTC
{
  public static class WebApiConfig
  {
    public static void Register(HttpConfiguration config) {
      // Web API configuration and services
      // Web API routes
      config.MapHttpAttributeRoutes();
      config.Routes.MapHttpRoute(
          name: "DefaultApi",
          routeTemplate: "api/{controller}/{id}",
          defaults: new { id = RouteParameter.Optional }
      );
    }
  }
}

This code adds a new route template to the routes accepted by this MVC application. Any call that starts with “api” is assumed to be a Web API call and thus the ASP.NET engine knows to look for an class with the specified controller name, and that inherits from the ApiController class.

Fix the Global.asax

If you open the \Models\Product class you see a standard C# class definition. As you can imagine, you are going to have a similar class on the client-side that maps all properties one-to-one from this C# class to the TypeScript class. However, the C# class uses pascal-casing of properties, while our TypeScript class is going to use camel-casing. You can add code to the Application_Start method in the Global.asax file to automatically convert properties from pascal-case to camel-case. Open the Global.asax file and at the top of the file add the three using statements shown in this code snippet.

using Newtonsoft.Json.Serialization;
using System.Web.Http;
using System.Net.Http.Formatting;

Add a single line of code to the Application_Start method to call the WebApiConfig.Register() method you just wrote. Place this line of code before you call the RouteConfig.RegisterRoutes() method.

protected void Application_Start() {
  AreaRegistration.RegisterAllAreas();
  FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
  // Add Web API
  GlobalConfiguration.Configure(WebApiConfig.Register);
  RouteConfig.RegisterRoutes(RouteTable.Routes);
  BundleConfig.RegisterBundles(BundleTable.Bundles);      
}

Next, add code after all the registration calls to perform the pascal-case to camel-case conversion for you.

// Get Global Configuration
HttpConfiguration config = GlobalConfiguration.Configuration;
// Convert to camelCase
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

The above code selects the current GlobalConfiguration.Configuration property and assigns it a variable called config. The next line of code queries the Formatters collection and retrieves the first instance of a JsonMediaTypeFormatter object it finds. Finally, you create a new instance of a CamelCasePropertyNamesContractResolver and assign that to the formatter’s ContractResolver property. This property controls how the JSON objects are formatted and sent to the client-side caller.

Build Client-Side Product List

Now that you have your server-side pieces in place, and have gotten Angular to work in your project, you can start building the HTML and classes to create a list of products. There are two new files you are going to create in this part of the article. One is a Product class and the other is a Product Service class.

Create Product Class

As you are going to retrieve a collection of Product classes from the Web API, you need a Product class written in TypeScript. Right mouse click on the \app\product folder and select Add | TypeScript file from the context-sensitive menu. Give this new file the name of product.ts. Add the following code in this file.

export class Product {
productId: number;
productName: string;
introductionDate: Date;
price: number;
url: string;
categoryId: number;
}

Notice that the structure of this class is exactly like the Product class that the Entity Framework generated. The only difference is the use of camel-case instead of pascal-case. But, as you remember we added code in the Global.asax to handle this conversion for us.

Create Product Service

We need an Angular product service to call the Web API controller you created earlier. This product service will retrieve all products. Right mouse click on the \app\products folder and select New | TypeScript file from the menu. Set the name to product.service.ts and click the OK button. Add the code shown in Listing 1.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { Product } from "./product";
@Injectable()
export class ProductService {
private url = "api/productApi";
constructor(private http: Http) {
}
getProducts(): Observable<Product[]> {
return this.http.get(this.url).map(this.extractData).catch(this.handleErrors);
}
private extractData(res: Response) {
let body = res.json();
return body || {};
}
  private handleErrors(error: any): Observable<any> {
    let errors: string[] = [];
    switch (error.status) {
      case 404: // Not Found
        errors.push("No Product Data Is Available.");
        break;
      case 500: // Internal Error
        errors.push(error.json().exceptionMessage);
        break;
      default:
        errors.push("Status: " + error.status 
                    + " - Error Message: " 
                    + error.statusText);
        break;
    };
    console.error('An error occurred', errors);
    return Observable.throw(errors);
  }
}

Listing 1: The product service retrieves a collection of products from your Web API.

Let’s break down the code shown in Listing 1. You first import the various classes and functions you need for this service using the ‘import’ keyword.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { Product } from "./product";

The injectable class is needed to support the use of the @Injectable decorator. This decorator informs Angular that this class may be injected into the constructor of any component. The Http and Response classes are used to access the Web API and to retrieve the response from the Web API. Next is the Observable extension function supplied by RxJS or Reactive Extensions for JavaScript. This is a library that helps you transform, compose and query any type of data. In the ProductService class, you use it to take the result set of data you retrieve from the Web API and turn it into an observable collection of data. Next are a series of different reactive extensions to support the map, catch and throw functions you use in this class. The last import is the Product class you created. Since you want to return an array of product objects returned from the Web API, you must import this class.

Angular will inject the Http service into our ProductService class. Any class marked with the @Injectable decorator may be injected in any class by declaring it in the constructor.

constructor(private http: Http) {
}

The next two functions in our class are getProducts and extractData. The getProducts function calls our Web API using the get function of the http service. It creates an observable array of products using the map extension function. If data is retrieved, the ‘map’ function is called and passed a reference to the extractData function. You don’t really need the extractData function, but I like having this function as it makes it easy for me to set a breakpoint in this function to see the data that has been returned. The extractData function is passed in the HTTP response object. Call the json function on this response object to retrieve the array of product data.

getProducts(): Observable<Product[]> {
return this.http.get(this.url).map(this.extractData).catch(this.handleErrors);
} private extractData(res: Response) { let body = res.json(); return body || {}; }

The last function in this class is handleErrors. 

private handleErrors(error: any): Observable<any> {
  let errors: string[] = [];
  switch (error.status) {
    case 404: // Not Found
      errors.push("No Product Data Is Available.");
      break;
    case 500: // Internal Error
      errors.push(error.json().exceptionMessage);
      break;
    default:
      errors.push("Status: " + error.status 
                  + " - Error Message: " 
                  + error.statusText);
      break;
  };
  console.error('An error occurred', errors);
  return Observable.throw(errors);
}

This function should be used to publish the error information. I am just going to output the error to the console window of the browser. You inform the calling code of the error by calling the throw function on the Observable class. The Observable returns an array of strings. For this sample, you only need a single error message to be returned, however, in other cases you will want multiple error messages. You will see examples of this when handling validation error messages.

Update the app.module.ts File

In order to inject the HTTP service in the ProductService class you need to import the HttpModule in the app.module.ts file. You then add this imported module to the @NgModule imports property. This makes it available for use in any of your classes. Angular will take care of injecting this service whenever it sees a reference to Http as you saw in the constructor of your ProductService class.

import { HttpModule } from '@angular/http';

@NgModule({
  imports: [BrowserModule, AppRoutingModule, HttpModule ]
  ...
})

Remember you marked your ProductService as an @Injectable class. This means you want Angular to automatically inject this service into any of your class constructors. To accomplish this, you also need to import the ProductService in your app.module.ts file and add this class to the providers property.

import { ProductService } from "./product/product.service";

@NgModule({
  imports: [BrowserModule, HttpModule ],
  declarations: [ AppComponent ],
  bootstrap: [AppComponent],
  providers: [ProductService]
})

Update Product List HTML Page

It is now finally time to build the components needed to display a list of products like the page shown in Figure 1. Open the \app\product\product-list.component.html you built earlier. Add the HTML shown in Listing 2.

<h2>Product List</h2>

<div class="row"
     *ngIf="products && products.length">
  <div class="col-xs-12">
    <div class="table-responsive">
      <table class="table table-bordered
                    table-condensed table-striped">
        <thead>
          <tr>
            <td>Product Name</td>
            <td>Introduction Date</td>
            <td>URL</td>
            <td class="text-right">Price</td>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let product of products">
            <td>
              {{product.productName}}
            </td>
            <td>
              {{product.introductionDate | date }}
            </td>
            <td>
              {{product.url}}
            </td>
            <td class="text-right">
              {{product.price | currency:'USD':true }}
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

<div class="row"
     *ngIf="messages && messages.length">
  <div class="col-xs-12">
    <div class="alert alert-warning">
      <ul>
        <li *ngFor="let msg of messages">{{msg}}</li>
      </ul>
    </div>
  </div>
</div>
Listing 2: Build the HTML table using the Angular *ngFor directive.

In the HTML that builds the product table, use the *ngIf directive to test if there any products. If there are, then you build an HTML table using the *ngFor directive to iterate over a collection named products in the ProductListComponent class. Each time through the loop a product object is assigned to a local variable named product. Use this product variable to retrieve each property of the Product class and display the value of each in each cell of the table.

At the end of the HTML is another <div> element which displays any error messages returned from the ProductListComponent class. This message is set if an error occurs in the retrieval of the product data from the Web API.

Update Product List Component

From the HTML in Listing 2 you know that your ProductListComponent class (Listing 3) must expose two properties to the HTML page; products and message. This class must be injected with the ProductService class from which you can request the product data. This class must also have some exception handling in case the Web API throws an exception or does not return data. Open the product-list.component.ts under the \app\product folder. Modify this class to look like the code contained in Listing 3.

import { Component, OnInit } from "@angular/core";

import { Product } from "./product";
import { ProductService } from "./product.service";
@Component({
  moduleId: module.id,
  templateUrl: "./product-list.component.html"
})
export class ProductListComponent implements OnInit {
  constructor(private productService: ProductService) {
  }
  ngOnInit(){
    this.getProducts();
  }
  // Public properties
  products: Product[] = [];
  messages: string[] = [];
  private getProducts(){
    this.productService.getProducts()
      .subscribe(products => this.products = products,
       errors => this.handleErrors(errors));
  }
  private handleErrors(errors: any) {
    for (let msg of errors) {
      this.messages.push(msg);
    }
  }
}

Listing 3: The product listing component.

Let’s look at each piece of the ProductListComponent class and learn why you need each line of code, and what each does. The first step is to import the various classes you use within this class. The following four imports are used in this class.

import { Component, OnInit } from "@angular/core";
import { Product } from "./product";
import { ProductService } from "./product.service";

You can see the Component import is needed for the @Component decorator. The templateUrl property in the decorator points to the product-list.component.html page you just created.

The OnInit import is needed to support the ngOnInit lifecycle event which is raised when the Angular engine creates an instance of this class. We use the ngOnInit function to call the getProducts function to retrieve the product data from the Web API.

The last two imports are easy to understand; we need the Product class because we are expecting to fill an array of Product objects from our call to the Web API. The ProductService class is used to call that Web API.

The constructor for this class receives the ProductService class from Angular injection and assigns that instance to the private property named productService.

constructor(private productService: ProductService) {
}

Two properties are declared in this class; products and messages. The products property is an array of Product objects. Initialize this property to an empty array by using the = []; syntax after the declaration of this property. The messages property is a string array used to display any error messages on the page.

// Public properties
products: Product[] = [];
messages: string[] = [];

The getProducts function calls the getProducts function from the ProductService class. This function returns an observable array of products from the map function, so use the subscribe function to retrieve the product array and assign it to the products property in this class. If an error occurs when calling the Web API, the function in the second parameter to the subscribe function is called. Take the error object passed back from the ProductService and pass that to the handleErrors function in this class.

getProducts() {
  this.productService.getProducts()
    .subscribe(products => this.products = products,
     errors => this.handleErrors(errors));
}

The handleErrors function simply loops through the list of error messages sent by the Observable.throw() in the Product service and adds each message to the messages array. This array is bound to an unordered list on the product-list.component.html page.

handleErrors(errors: any) {
  for (let msg of errors) {
    this.messages.push(msg);
  }
}

Update ProductController

Now that you are retrieving data from the Web API you created, you no longer need the code in the Get() or the Post() methods in your ProductController class. Open the ProductController class and delete the Post() method. Modify the Get() method to look like the following.

public ActionResult Product() {
    return View();
}

Summary

In these last two blog posts you added Angular into an MVC application. You learned to use Angular routing to route from MVC to Angular. You created a Web API on your server to return data to an Angular service. You then displayed a list of the data returned on an HTML page. In the next blog post you learn to add a new product.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Angular to MVC – Part 2. NOTE: After downloading the sample, you will need to right mouse click on the package.json file and select the menu “Restore Packages”. Create the product by locating the \SqlScripts\Product.sql file and run the script in a SQL Server database. Open the Web.config file in the project and update the connection string to point to your server and database name.

Add Angular to MVC – Part 1

Many of us have MVC applications currently running. You would like to start using Angular 2 in your web applications, but don’t have the time to completely rewrite your MVC application. It would be nice if you could just re-write one or two pages in Angular and keep the rest of your MVC project in place. Turns out you can. In this blog post, you will learn how to add Angular to your MVC applications. For this post, I am assuming you are a Microsoft Visual Studio developer and are familiar with MVC, Angular, C#, and the Web API.

Setup Your Machine

Before you begin to use Angular, you must prepare your development machine. There are two tools required, as well as you must configure Visual Studio 2015 to use TypeScript. You should also get the Angular quick start project.

Install Node

If you have not done so already, download and install Node.js and NodeJS Package Manager (npm). You can get these two packages at https://nodejs.org/en/download/. Follow the instructions for downloading and installing these tools on nodejs.org.

Configure Visual Studio 2015

Most developers are using TypeScript for Angular development. Ensure you have downloaded and installed TypeScript 2.x for Visual Studio 2015. Select the Tools | Extensions and Updates menu to bring up the Extensions and Updates window (Figure 1) for Visual Studio. Click on the Installed node in the tree view and look through your list and see if you have installed TypeScript 2.x. If you have an older version of TypeScript you need to update it.


Figure 1: Use the Extensions and Updates window to check for TypeScript.

To update to TypeScript 2.x, click on the Online node in the tree view and perform a search for TypeScript. Locate the latest version of TypeScript and download and install it into Visual Studio.

One last configuration item for Visual Studio is to select the Tools | Options menu and expand the Projects and Solutions node. Click on the External Web Tools (Figure 2). Ensure that the $(PATH) variable is located above any $(DevEnvDir) variables if they exist in your environment. In my installation of Visual Studio, these variables did not exist.


Figure 2: Make sure the $(PATH) variables is high in your location of external tools.

Download Angular Quick Start

Now that you have configured Visual Studio, you are going to need some configuration files and some TypeScript files to make it easier to get started with Angular. The Angular team has created a sample project that contains these files. This sample project is not a Visual Studio project, so you are only going to use some of the files from this project and not bring all of them into your project. Download the quick start zip file from https://github.com/angular/quickstart. After the zip file has been downloaded, unzip the files into a folder on your hard drive.

Sample MVC Application

Let’s look at a sample MVC application, named PTC, that I am going to use as a demonstration (Figure 3). You can download this sample, and the final sample by following the instructions at the end of this blog post.


Figure 3: A sample MVC application to display a product list

This is a vanilla MVC application that does not have the Web API in it. It has the following classes that are of interest.

  • ProductController- The MVC controller to display the \Views\Product\Product.cshtml page.

  • Product - An entity class to represent a product object
  • PTCData-Extensions - Additional validation for Product data
  • PTCData - Entity Framework generated data layer.

  • PDSAPageModeEnum - Enumeration for the mode that page is currently in
  • ProductViewModel - A view model that is called from the ProductController to retrieve data

  • _ProductDetail.cshtml - A partial page to display, add, and edit a single product record.
  • _ProductList.cshtml - A partial page that displays a list of product data
  • Product.cshtml - A MVC page to list or edit product(s) retrieved from the view model.

Install Product Table

Before you can run this sample, you need to download the sample project, locate the \SqlScripts folder and install the Product.sql into a SQL Server database. After you have created the Product table, open the Web.config file and adjust the connection string to point to your server and your database.

Test the Page

You can now run the Product.cshtml page and ensure you get something that looks like Figure 4.


Figure 4: The product list page

Add Angular Files

You need to add a few Angular files from the quick start project you downloaded. Prior to doing this, add a new folder named \app to your MVC project. Using Windows Explorer, navigate to the folder where you extracted the quick start files. Do NOT copy all these files into your Visual Studio project, you do not need all of them. Locate and copy the following files into the root of your new Visual Studio project.

  • \package.json
  • \tslint.json
  • \src\tsconfig.json

Copy the file \src\systemjs.config.js into the \scripts folder of your VS project.

Copy the file \src\main.ts into the \app folder of your VS project. You will most likely receive a message that your project has been configured to support TypeScript. It will ask you if you want to search for TypeScript typings. Just answer No as you are not going to need these right now.

Expand the \src\app folder in the quick start folder and copy the following TypeScript files located in this folder into the \app folder you created in your VS project. 

  • app.component.ts
  • app.module.ts

Restore Packages

Even though you added some TypeScript files, nothing is going to work yet. You need to download a new folder using the NodeJS Package Manager (npm). Visual Studio can download all of these files using npm, but you must first close and reopen Visual Studio. If you don’t close and reopen Visual Studio, the appropriate menu item needed will not show up. Go ahead and close your instance of Visual Studio now, then, reopen the ProductApp project. Right mouse click on the package.json and select Restore Packages (Figure 5) from the menu. This process takes a little while, so be patient.


Figure 5: The Restore Packages menu only shows up after closing and reopening Visual Studio.

After the installation of the packages is complete, click on the Show All Files icon in the Solution Explorer window. You should see a new folder has appeared named node_modules (Figure 6). DO NOT include this folder in your project, you do not need all these files in your project. They are just there to support the various libraries you use when developing with Angular.


Figure 6: Don’t include the node_modules folder in your project.

Add Links to the MVC Shared Layout

Navigate to where you installed the quick start files and open the \src\index.html page. You need to copy a few lines of code from this page into the \Views\Shared\_Layout.cshtml file. Open the _Layout.cshtml file in Visual Studio. Locate the following lines of code in the index.html page and paste them into the <head> section of the _Layout.cshtml file.

<base href="/">

I like to the above line of code as the first one after the <head> element. Locate the next lines that start with the comment shown below and copy them all after the @Scripts.Render() and before the </head> element.

<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
  System.import('main.js')
       .catch(function(err){ console.error(err); });
</script>

Since the _Layout is in a different location in your project from the index.html in the quick start project, you need to fix up the src attributes. Add a forward slash in front of each of the above references for the node_modules.

<script src="/node_modules/core-js/client/shim.min.js"></script>
<script src="/node_modules/zone.js/dist/zone.js"></script>
<script src="/node_modules/systemjs/dist/system.src.js"></script>

For the systemjs.config.js file, you placed that into the Scripts folder, so you need to add /scripts/ in front of that file reference.

<script src="/scripts/systemjs.config.js"></script>

Locate the <script> tag that is responsible for importing the main.js file which is transpiled from the main.ts file. This code is shown in the following code snippet.

<script>
  System.import('main.js').catch(function(err){ console.error(err); });
</script>

Since you moved the main.ts file into the \app folder, modify the parameter passed to the import function to ‘app/main’ as shown in the following snippet.

<script>
  System.import('/app/main').catch(function(err){ console.error(err); });
</script>

In the quick start project, the main.ts file was expecting to find the app.module.ts file in the \app folder below the root folder. However, since these two files are now located in the same folder, you need to open the main.ts file located in the \app folder and locate the following line of code.

import { AppModule } from './app/app.module';

Remove the ‘/app’ portion from this line of code so that the final code looks like the following snippet.

import { AppModule } from './app.module';

You should run your Product page again just to make sure that your application still compiles and runs.

Eliminate Hard-Coded HTML Template

The app.component.ts file contains a template property to output some HTML. I do not like having HTML written in TypeScript, so create an HTML page to display the HTML for the landing page of your Angular application.

Right mouse click on the \app folder and select Add | HTML Page and set the name to app.component.html. Click the OK button. Remove the HTML in the page and replace it with the following:

<router-outlet></router-outlet>

In the HTML above you are adding a location for the Angular routing engine to place HTML that comes from different components. For example, you are going to create a product list component and corresponding HTML page to display product data. That HTML will be placed in between these two tags.

Open the \app\app.component.ts file and you will see the following declaration.

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>`,
})

Now that you have created the new HTML page, you are going to remove the hard-coded HTML from the template property in the @Component decorator. I have found that if you have any more than a few lines of HTML, this property becomes cumbersome to maintain. Let’s change the template property to the templateUrl property and set that property to the string ‘app.component.html’.

@Component({
  moduleId: module.id,
  selector: 'my-app',
  templateUrl: './app.component.html'
})

Another change you are making to this @Component decorator is adding the property moduleId and setting that property to the value module.id. This property helps Angular understand relative paths in relation to the current component. If you did not use the moduleId property, then you change the templateUrl property to ‘./app/app.component.html’. I prefer to use relative paths as opposed to having to fully quality the path in relation to the root.

The AppComponent class that came with the quick start sample has a name property set to the word ‘Angular’. Just delete this code and make your AppComponent class look like the following:

export class AppComponent { }

You will learn more about how the routing works a little later in this article. But first, let’s build the product list component and HTML.

Stub the Angular Product Components

To test that you can route to a Product list page from your MVC page, create a couple of new files.

Right mouse click on the \app folder and add a new folder called \product. Right mouse click on this product folder and add a new HTML page called product-list.component.html. Delete all the HTML in this new page and add the single line below:

<h2>Product List</h2>

Right mouse click on the product folder and add a new TypeScript file named product-list.component.ts. Add the following code:

import { Component } from "@angular/core";

@Component({
  moduleId: module.id,
  templateUrl: "./product-list.component.html"
})
export class ProductListComponent { }

Update the app.module.ts File

Add the ProductListComponent class to the app.module.ts. Open the app.module.ts file and add an import near the top of the file that looks like the following.

import { ProductListComponent } from "./product/product-list.component";

In the @NgModule decorator, add the ProductListComponent class to the declarations property by adding a comma after AppComponent and typing in the name of this class as shown below.

declarations: [ AppComponent, ProductListComponent ]

Angular Routing

No Angular application would be complete without routing. Routing allows us to navigate from one page to another. The app.component.html page you just created is the main page for getting to other pages in our application via the routing engine. Add a new TypeScript file called app-routing.module.ts and add the code shown in Listing 1.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProductListComponent } from "./product/product-list.component";
const routes: Routes = [
{
    path: 'productList',
    component: ProductListComponent
  },
  {
    path: 'Product/Product',
    redirectTo: 'productList'
  }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Listing 1: The routine class defines one or more routes for your application.

The important part of the app-routing.module.ts file is the constant named routes. This constant contains an array of Route objects. Each object has a path property which is used in the routerLink attribute you created earlier. If the path matches the address in the browser, then the component property is used to instantiate an instance of the object listed in this property. Once this class is instantiated, the HTML defined in that classes templateUrl property is inserted into the location where the <router-outlet> directive is located.

The routes constant is added to the singleton instance of the Router service using the forRoot function. You can see this in the imports property of the @NgModule decorator. You can add as many routes to the routes constant as you need for your application.

Overview of Angular Routing

With all that description above, I thought a graphical representation (Figure 7) of the Angular routing you are using in this application might help clear things up a little.


Figure 7: An overview of the Angular routing process.

  1. Anytime the browser address bar is updated via an anchor tag, or other mechanism, the last part of the address is matched up to one of the routes added to the singleton instance of the Router service.
  2. Once it finds the path in the Router service, it instantiates the class listed in the component property.
  3. After the class is instantiated, the templateUrl property is read from the @Component decorator.
  4. The HTML from the file listed in the templateUrl property is loaded and inserted into the DOM at the location of the <router-outlet> directive.

Update the app.module.ts File

Just as you have done with all your other classes you created, you need to declare your intention to use routing in the app.module.ts file. Open the app.module.ts file and add the following import statement near the top of the file.

import { AppRoutingModule } from './app-routing.module';

Add the AppRoutingModule to the imports property in the @NgModule decorator. The imports property should now look like the following code snippet.

imports: [BrowserModule, AppRoutingModule ]

Call our Angular page from the Product.cshtml page

Open the \Product\Product.cshtml page and delete all the code. Write the following:

<my-app>Loading products...</my-app>

Remember the <my-app> is the selector in the app.component.ts file. When this selector is encountered, the Angular system kicks in and starts loading our Angular components to run. You have now successfully removed yourself from the MVC system and are now running all client-side!

Run the Page

At this point you should be able to run the page and see the <h1> you entered. Your page should like Figure 8.


Figure 8: You have rerouted to an angular page from an MVC page.

Summary

Congratulations! You have successfully integrated Angular into a Visual Studio MVC Application. There are a lot of steps to get Angular up and running, however, once you get the basics configured, adding new pages and new components is quite easy because you use the same pattern you learned in this post. In the next blog post, you add and call a Web API from an Angular service, and display the product data in an HTML page using Angular.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Angular to MVC - Part 1. To run the sample, locate the \SqlScripts\Product.sql file and run the script in a SQL Server database. Open the Web.config file in the project and update the connection string to point to your server and database name.

Caching for Non-Web Applications – Part 2

In my last blog post, entitled Caching for Non-Web Applications – Part 1 I introduced you to the MemoryCache class. This class allows you to create a cache for any type of application just like in ASP.NET applications. The great thing about the MemoryCache class is there is no reliance on the System.Web namespace. This means you are free to use the MemoryCache class in any type of application such as Windows Forms, WPF and Windows Services. The last blog post showed you how to add and retrieve data from the cache. This blog post expands on the last one and shows you additional methods you can take advantage of to work with cache data.

Add or Get Existing

The AddOrGetExisting method has the same signature as the Add method. However, instead of returning a true or false value, it will either return a null if the key/value pair was added to the cache, or returns the object if the key value is found already in the cache.

object ret;
ret = MemoryCache.Default.AddOrGetExisting("Key2", 
         "Value 2", DateTimeOffset.Now.AddSeconds(5));
if (ret == null) {
MessageBox.Show("Key did NOT exist");
}
else {
MessageBox.Show("The Key did already exist");
}

AddOrGetExisting Using CachePolicy

Just like you are able to add a new item to the cache using the CachePolicy using the Add method, you can do the same with the AddOrGetExisting method. The code below will add a new cache item with a sliding expiration of 5 seconds.

object ret;
CacheItemPolicy pol = new CacheItemPolicy();
pol.SlidingExpiration = new TimeSpan(0, 0, 5);
ret = MemoryCache.Default.AddOrGetExisting("Key2", 
                   "Value 2", pol);
if (ret == null) {
MessageBox.Show("Key did NOT exist");
}
else {
MessageBox.Show("The Key did already exist");
}

Set Method

The Set method adds an entry into the cache if the specified key value does not exist. However, if the key value does exist, it will update the value for that key in the cache. I prefer to use this method over the Add or AddOrGetExisting as I typically just want to either add, or update, and not worry about whether I have created the value previously or not. This method does not return any value.

MemoryCache.Default.Set("Key3", "Value 3", 
     DateTimeOffset.Now.AddSeconds(5));

You can use a CacheItemPolicy to set a sliding expiration on the item to cache.

CacheItemPolicy pol = new CacheItemPolicy();
pol.SlidingExpiration = new TimeSpan(0, 0, 5);
MemoryCache.Default.Set("Key3", "Value 3", pol);

Notify Before Removal from Cache

Just before an item is to be removed from the cache because it is expiring, you may create a method to respond to this event. Write the following method to accept a CacheEntryUpdateArguments object. Within this method body you can write whatever code you want. In the sample below I am just writing out the Key and the reason for the removal.

private void UpdateHandler(CacheEntryUpdateArguments e) {
Console.WriteLine(e.Key);
Console.WriteLine(e.RemovedReason.ToString());
}

Once you have this method written, you assign that method to the UpdateCallback property of a CacheItemPolicy object. The code snippet below shows you how you assign this method to the UpdateCallback property.

CacheItemPolicy pol = new CacheItemPolicy();
pol.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(3);
pol.UpdateCallback = UpdateHandler;
MemoryCache.Default.Set(THE_KEY, "Update 3", pol);

If you run the above code and wait a few seconds, you will see a couple of messages printed in the Output window.

Within the UpdateHandler method you also have the option to change the value in the cache and place it back into the cache. You do this by assigning a new CacheItem object with the same key and a value to the e.UpdatedCacheItem property. You must also set the e.UpdatedCacheItemPolicy property to a new CacheItemPolicy object. When this method ends, the item is not removed and is updated within the cache. The code to do this is shown below.

private void UpdateHandler(CacheEntryUpdateArguments e) {
Console.WriteLine(e.Key);
Console.WriteLine(e.RemovedReason.ToString());
e.UpdatedCacheItem = new CacheItem(e.Key, "My New Value");
CacheItemPolicy pol = new CacheItemPolicy();
pol.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10);
pol.UpdateCallback = UpdateHandler;
e.UpdatedCacheItemPolicy = pol;
}

Notify After Removal from Cache

If you do not care to update an item in the cache, you may just respond to the RemovedCallback event. The design pattern is similar to what you just wrote in the previous example. First create a method that accepts a CacheEntryRemovedArguments object. The only two properties in that object you care about are the e.RemovedReason and the e.CacheItem. The e.CacheItem object contains the item that was just removed from the cache.

private void RemovedHandler(CacheEntryRemovedArguments e) {
  Console.WriteLine(e.RemovedReason.ToString());
  Console.WriteLine("Removed Handler: " + e.CacheItem.Key);
}

With the RemovedHandler method created, you may now set the RemovedCallback property of a CacheItemPolicy object. You then use that CacheItemPolicy object to add a new item into the MemoryCache object.

CacheItemPolicy pol = new CacheItemPolicy();
pol.SlidingExpiration = new TimeSpan(0, 0, 3);
pol.RemovedCallback = RemovedHandler;
MemoryCache.Default.Set("Key3", "Remove 3", pol);

If you run the above code, then either let the value expire, or explicitly remove the item from cache, the RemovedHandler method is called.

Summary

In this blog post you learned to call the AddOrGetExisting method. This method is different from the Add method as it either adds an item to the cache, or retrieves one if they key already exists. You also learned to use the Set method to either add a new item to the cache, or update an item in the cache if it already exists. Providing a couple of callback functions to the CacheItemPolicy lets you respond when an item is about to be deleted from the cache, or after it has already been deleted.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Caching for Non-Web Applications.