March 2011

Volume 26 Number 03

RIA Frameworks - Building Data-Centric Web Apps with ASP.NET MVC and Ext JS

By Juan Carlos | March 2011

A rich Internet application (RIA) combines the usability of a desktop app with the flexibility of Web-based deployment and revision. There are two key approaches to building RIAs. First, there are browser plug-ins that host execution environments such as Flash, Java and Silverlight. Second, there are JavaScript-based extension libraries such as Dojo, Ext JS, jQuery, MooTools, Prototype and YUI. Each approach has its advantages and disadvantages.

JavaScript libraries are a popular choice for building RIAs because JavaScript is supported by all major browsers and there’s no need to install a plug-in or runtime environment. I’ve been experimenting with another of the libraries mentioned—Ext JS—and I’ve found that it makes an interesting choice for implementing Web apps. It’s easy to implement, well-documented and is compatible with Selenium for testing. Ext JS also provides pre-defined controls that simplify creating the UI of your Web app.

Unfortunately, most examples of Ext JS are illustrated with PHP, Python and Ruby on Rails code on the server side. But that doesn’t mean developers using Microsoft technologies can’t take advantage of Ext JS. While it’s difficult to integrate Ext JS with Web Forms development (due to the abstraction layer that encapsulates the request-response nature of the Web in order to provide a stateful control-based model), you could use the ASP.NET MVC framework, enabling you to leverage both the Microsoft .NET Framework and Ext JS in the same app.

In this article, I’ll provide the tutorial I couldn’t find, walking through the development of a real-world Web solution using ASP.NET MVC and Ext JS that reads from and writes to a back-end database.

Ext JS Form Basics

To use Ext JS, you first need to download it from sencha.com. (I used version 3.2.1, but you should grab the most recent version.) Note that a free, open source version of Ext JS is available for open source projects, non-profit organizations and educational use. For other uses you may need to purchase a license. See sencha.com/products/license.php for more information.

Uncompress the download into a directory in your file system. It contains everything you need to develop a Web solution using Ext JS, in particular the main file ext-all.js. (There’s also a debug version to help find errors more easily.) Dependencies, documentation and example code are all included in the download.

The required folders for a project are \adapters and \resources. The adapters folder enables use of other libraries alongside Ext JS. The resources folder contains dependencies such as CSS and images.

To use Ext JS correctly, you’ll also need to include the three key file references in your pages:

ext-3.2.1/adapter/ext/ext-base.js
ext-3.2.1/ext-all.js
ext-3.2.1/resources/css/ext-all.css

The ext-base.js file contains the core functionality of Ext JS. The widget definitions are contained in ext-all.js, and ext-all.css includes stylesheets for the widgets.

Let’s start using Ext JS in a static HTML page to illustrate the basics. The following lines are contained within the head section of the page and link the required files to successfully develop an Ext JS solution (I’ve also included the JavaScript module with some example widgets from the Ext JS download):

<link rel="stylesheet" type="text/css" 
  href="ext-3.2.1/resources/css/ext-all.css" />
<script type="text/javascript" language="javascript" 
  src="ext-3.2.1/adapter/ext/ext-base.js"></script>
<script type="text/javascript" language="javascript" 
  src="ext-3.2.1/ext-all.js"></script>
<script type="text/javascript" language="javascript" 
  src="extjs-example.js"></script>

Within the body of the file, I insert a div element for rendering the main Ext JS form:

<div id="frame">
</div>

The extjs-example.js file provides some insight into how Ext JS applications are constructed. The template for any Ext JS application uses the Ext.ns, Ext.BLANK_IMAGE_URL and Ext.onReady statements:

Ext.ns('formextjs.tutorial');
Ext.BLANK_IMAGE_URL = 'ext-3.2.1/resources/images/default/s.gif';
formextjs.tutorial.FormTutorial = {
  ...
}
Ext.onReady(formextjs.tutorial.FormTutorial.init, 
  formextjs.tutorial.FormTutorial);

The Ext.ns statement enables you to logically organize your code in a namespace, in order to avoid naming conflicts and scoping problems.

The Ext.BLANK_IMAGE_URL statement is important for rendering the widgets. It’s called the spacer image (a transparent 1x1 pixel image) and mainly used to generate the blank space as well as to place icons and separators.

The Ext.onReady statement is the first method to define with Ext JS code. This method is automatically called once the DOM is fully loaded, guaranteeing that every HTML element that you may reference is available when the script runs. In the case of extjs-example.js, here’s the script itself:

formextjs.tutorial.FormTutorial = {
  init: function () {
    this.form = new Ext.FormPanel({
      title: 'Getting started form',
      renderTo: 'frame',
      width: 400,
      url: 'remoteurl',
      defaults: { xtype: 'textfield' },
      bodyStyle: 'padding: 10px',
      html: 'This form is empty!'
    });
  }
}

An instance of the class Ext.FormPanel is created as a container for the fields. The renderTo property points to the div element where the form will be rendered. The defaults property specifies the default component type on the form. The url property specifies the URI to send the request of the form. Finally, the html property specifies the text (with any HTML formatting) as the default output.

To add fields, you need to replace the html property with the items property:

items: [ nameTextField, ageNumberField ]

The first two items to add are a text field and a number field:

var nameTextField = new Ext.form.TextField({
  fieldLabel: 'Name',
  emptyText: 'Please, enter a name',
  name: 'name'
});
var ageNumberField = new Ext.form.NumberField({
  fieldLabel: 'Age',
  value: '25',
  name: 'age'
});

The required properties are: fieldLabel property (to set a descriptive message accompanying the component of the form) and name property (to set name of the request parameter). The emptyText property defines the watermark text that the field will contain when it’s empty. The value property is the default value for the control.

Another way to declare controls is on the fly:

items: [
  { fieldLabel: 'Name', emptyText: 'Please, enter a name', name: 'name' },
  { xtype: 'numberfield', fieldLabel: 'Age', value: '25', name: 'age' }
]

As you can see, for the name field you don’t have to specify the type because it’s taken from the default properties of the form.

I’ll add some additional elements to the form, which ends up looking like Figure 1.

image: The Completed Form

Figure 1 The Completed Form

So far, you’ve built a form using Ext JS to take data from the user. Now, let’s send the data to the server. You’ll need to add a button to handle the submit process and show the result to the user, as shown in Figure 2.

Figure 2 Form Buttons

buttons: [{
  text: 'Save', 
  handler: function () {
    form.getForm().submit({
      success: function (form, action) {
        Ext.Msg.alert('Success', 'ok');
      },
      failure: function (form, action) {
        Ext.Msg.alert('Failure', action.result.error);
      }
    });
  }
},
{
  text: 'Reset',
  handler: function () {
    form.getForm().reset();
  }
}]

The buttons property enables the form to manage all the possible actions. Each button has name and handler properties. The handler property contains the logic associated with the action executed on the button. In this case, there are two buttons whose names are Save and Reset. The Save button handler executes a submit action on the form and shows a message indicating success or failure. The Reset button handler resets the field values on the form.

The last—but important—step in form creation is validation. In order to specify required fields, we need to set the allowBlank property to false and the blankText property to an error message that's displayed when the required validation fails. For example, here’s the name field of the form:

{ fieldLabel: 'Name', emptyText: 'Please, enter a name', name: 'name', allowBlank: false }

When you run the application and click the Save button without entering any data in the Name and Age fields, then you receive an error message and the required fields are underlined in red.

To customize the error messages on the fields, add the following line of code just under the Ext.onReady function:

Ext.QuickTips.init();

Now, when the user moves the mouse pointer over the field, a balloon with a message displaying the error is displayed.

I set several validation rules for the fields such as specifying the minimum and maximum length allowed, deferring the field validation until form submission, and creating validation functions for URLs, e-mail addresses, and other types of data. You can see the details of this validation in the code download.

Building the Web App

Now, let’s develop a Web solution using Ext JS and ASP.NET MVC. I used ASP.NET MVC 2, but this solution should be applicable to ASP.NET MVC 3 as well. The scenario I’m going to address is adding an employee in a human resources management system.

The Add Employee use case description is as follows: A screen prompts the user to enter valid information for a new employee such as employee identifier, full name, address, age, salary and department. The department field is a list of departments from which to choose.

The main implementation strategy is to create an Ext JS form on the client side—as you’ve already seen—and then process the data using ASP.NET MVC. The persistence layer will use LINQ to represent business entities and to persist data to the database system. The back-end database is Microsoft SQL Server 2008.

Start by opening Visual Studio 2010 and creating a new project with the ASP.NET MVC 2 Web Application template.

Next, create the database schema. For this example, the schema will contain two entities: employee and department. Figure 3 shows how I created the Human Resources database and the underlying tables and constraints.

Figure 3 Creating the Human Resources Database

create table department(
  deptno varchar(20) primary key,
  deptname varchar(50) not null,
  location varchar(50)
);
create unique index undx_department_deptname on department(deptname);
insert into department
  values('HQ-200','Headquarter-NY','New York');
insert into department
  values('HR-200','Human Resources-NY','New York');
insert into department
  values('OP-200','Operations-NY','New York');
insert into department
  values('SL-200','Sales-NY','New York');
insert into department
  values('HR-300','Human Resources-MD','Maryland');
insert into department
  values('OP-300','Operations-MD','Maryland');
insert into department
  values('SL-300','Sales-MD','Maryland');
create table employee(
  empno varchar(20) primary key,
  fullname varchar(50) not null,
  address varchar(120),
  age int,
  salary numeric(8,2) not null,
  deptno varchar(20) not null,
  constraint fk_employee_department_belong_rltn foreign key(deptno)
    references department(deptno)
);
create unique index undx_employee_fullname on employee(fullname);

Now let’s use LINQ to SQL to define the structure of the entities and the persistence mechanism. Start by creating an EmployeeRepository class to manage the data-access logic to the employee table. In this case, you only need to implement the create operation:

public class EmployeeRepository {
  private HumanResourcesDataContext _ctxHumanResources = 
    new HumanResourcesDataContext();
  public void Create(employee employee) {
    this._ctxHumanResources.employees.InsertOnSubmit(employee);
    this._ctxHumanResources.SubmitChanges();
  }
}

You also need a DepartmentRepository class to manage the data-access logic to the department table. Again, in this simple case you only need to implement the read operation in order to find a list of departments:

public class DepartmentRepository {
  private HumanResourcesDataContext _ctxHumanResources = 
    new HumanResourcesDataContext();
  public IQueryable<department> FindAll() {
    return from dept in this._ctxHumanResources.departments
           orderby dept.deptname
           select dept;
  }
}

Now let’s define another important piece of the architecture: the controller. To define a controller, right-click on the Controllers folder in the Solution Explorer window and select Add | Controller. I used HumanResourcesController as the controller name.

Ext JS Presentation Layer

Now let’s go back to Ext JS and use the framework to build the presentation layer of the application. For this solution, you only need to import ext-all.js and the \adapter and \resources folders.

Go to the Site.Master page and add references to the Ext JS files inside the head element, as well as an <asp:ContentPlaceHolder> tag element as a container of the customized JavaScript and CSS code for each page, as shown in Figure 4.

Figure 4 Site.Master

<head runat="server">
  <title><asp:ContentPlaceHolder ID="TitleContent" 
    runat="server" /></title>
  <link href="../../Content/Site.css" rel="stylesheet" 
    type="text/css" />
  <!-- Include the Ext JS framework -->
  <link href="<%= Url.Content("~/Scripts/ext-3.2.1/resources/css/ext-all.css") %>" 
    rel="stylesheet" type="text/css" />
  <script type="text/javascript" 
    src="<%= Url.Content("~/Scripts/ext-3.2.1/adapter/ext/ext-base.js") %>">
  </script>
  <script type="text/javascript" 
    src="<%= Url.Content("~/Scripts/ext-3.2.1/ext-all.js") %>">
  </script>    
  <!-- Placeholder for custom JS and CSS and JS files 
    for each page -->
  <asp:ContentPlaceHolder ID="Scripts" runat="server" />
</head>

Now let’s add the other important pieces of the MVC architecture: the view. The view will present the form to get the data related to one employee. Go to HumanResourcesController, right-click on the Index action method and select Add View. Click the Add button in the Add View dialog box.

To implement the Ext JS form created earlier in the article, you need to add a JavaScript file to the Scripts directory and a reference to this JavaScript file in the view. Then include the reference to the employee_form.js file and add a div element into the Index.aspx view (see Figure 5).

Figure 5 Adding the Employee Form

<%@ Page Title="" Language="C#" 
  MasterPageFile="~/Views/Shared/Site.Master" 
  Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" 
  runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" 
  runat="server">
  <h2>Add a New Employee</h2>
  <div id="employeeform"></div>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="Scripts" 
  runat="server">
  <script type="text/javascript" 
    src="<%= Url.Content("~/Scripts/employee_form.js") %>">
  </script>
</asp:Content>

Go to the employee_form.js file and add some code to configure the ExtJS form and its underlying widgets. The first step is to define an instance of Ext.data.JsonStore class to get a list of departments:

var departmentStore = new Ext.data.JsonStore({
  url: 'humanresources/departments',
  root: 'departments',
  fields: ['deptno', 'deptname']
});

The url property points to the departments action method on the HumanResourceController controller. This method is accessed by the HTTP POST verb. The root property is the root element of the list of departments. The fields property specifies the data fields. Now define the form. The properties are self-descriptive:

var form = new Ext.FormPanel({
  title: 'Add Employee Form',
  renderTo: 'employeeform',
  width: 400,
  url: 'humanresources/addemployee',
  defaults: { xtype: 'textfield' },
  bodyStyle: 'padding: 10px',

In this case, the url property points to the AddEmployee action method on the HumanResourceController controller. This method is also accessed using HTTP POST.

The items property provides the list of widgets representing the fields of the form (Figure 6). Here the default widget is a text field (this is specified in the defaults property). The first field is employee number, which is required (specified by the allowBlank property). The second field is the full name, which is also a required text field. The address field is an optional text area. The age field is an optional number field. The salary field is a required number field. And finally, the department number field is an identifier string, which is selected from a list of departments.

Figure 6 Form Field Widgets

items: [
  { fieldLabel: 'Employee ID', name: 'empno', allowBlank: false },
  { fieldLabel: 'Fullname', name: 'fullname', allowBlank: false },
  { xtype: 'textarea', fieldLabel: 'Address', name: 'address', 
    multiline: true },
  { xtype: 'numberfield', fieldLabel: 'Age', name: 'age' },
  { xtype: 'numberfield', fieldLabel: 'Salary', name: 'salary', 
    allowBlank: false },
  { xtype: 'combo', fieldLabel: 'Department', name: 'deptno', 
    store: departmentStore, hiddenName: 'deptno', 
    displayField: 'deptname', valueField: 'deptno', typeAhead: true,
    mode: 'remote', forceSelection: true, triggerAction: 'all', 
    emptyText: 'Please, select a department...', editable: false }
],

Finally, the buttons property is defined to handle the actions over the form. This is configured just like Figure 2, but the text property has the value “Add.”

Now the employee_form.js file is complete. (I’ve gone through most of the elements of the file here. See the code download for the complete source code listing for this file.)

Now let’s go to HumanResourceController and implement the corresponding action methods, as shown in Figure 7.

Figure 7 HumanResourceController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using HumanResources_ExtJS_ASPNETMVC.Models;
namespace HumanResources_ExtJSASPNETMVC.Models.BusinessObjects {
  public class HumanResourcesController : Controller {
    DepartmentRepository _repoDepartment = new DepartmentRepository();
    EmployeeRepository _repoEmployee = new EmployeeRepository();
    // GET: /HumanResources/
    public ActionResult Index() {
      return View();
    }
    // POST: /HumanResource/Departments
    [HttpPost]
    public ActionResult Departments() {
      var arrDepartment = this._repoDepartment.FindAll();
      var results = (new {
        departments = arrDepartment
      });
      return Json(results);
    }
    // POST: /HumanResource/AddEmployee
    [HttpPost]
    public ActionResult AddEmployee(employee employee) {
      string strResponse = String.Empty;
      try {
        this._repoEmployee.Create(employee);
        strResponse = "{success: true}";
      }
      catch {
        strResponse = "{success: false, error: \"An error occurred\"}";
      }
      return Content(strResponse);
    }
  }
}

That’s It!

Now run the solution. You’ll see the Web page shown in Figure 8. Enter some data in the form and then click Add. You’ll see a confirmation message box. You’ll also see the row inserted in the dbo.employee table on the database.

image: Running the Application

Figure 8 Running the Application

That’s really all there is to creating a simple RIA. Depending on the features you want to leverage, a similar application could be built with any of the other popular JavaScript frameworks while still employing ASP.NET MVC. You could easily substitute Entity Framework for the data layer, and use Azure storage or SQL Azure as the back-end data store. These simple building blocks make building a basic data-centric RIA quick and easy.


Juan Carlos Olamendy is a senior architect, developer and consultant. He has been recognized as a Microsoft Most Valuable Professional (MVP) and Oracle ACE several times. He is Microsoft Certified Technology Specialist in Windows Communication Foundation. You can contact Olamendy at johnx_olam@fastmail.

Thanks to the following technical experts for reviewing this article: Scott Hanselman and Eilon Lipton