Introduction
Recently I've been researching a lot about the use of ExtJS JavaScript framework
along with ASP.NET MVC in order to build very rich Web applications. ExtJS is a
cool JavaScript library, so I want to describe another good feature of it. In
this article, I want to talk about the creation of forms with combobox fields
using ExtJS and ASP.NET MVC. As an illustrative example, I'm going to display a
list of categories in a combobox item for the user to choose one of them. The
underlying data is persisted in the Production.ProductCategory table on the
AdventureWorks database shipped with SQL Server 2005/2008.
Getting started with the solution
The first step is to create a MVC application in Visual Studio.NET 2008 (see
Figure 1).
Figure 1
Next step is to add the ExtJS library files to the ASP.NET MVC project. Let's
add the following files and directories into the Scripts directory: ext-all.js
file, ext-base.js file and resources directory (see Figure 2).
Figure 2
Now let's include the ExtJS library files in our solution by going to the
Site.Master page and adding the references to the ExtJS files inside the head
element (highlighted in yellow in the Listing 1). As well, we need to add a <asp:ContentPlaceHolder>
tag element as container of the customized JavaScript and CSS code for each page
(highlighted in green in the Listing 1)
<head
runat="server">
<title><asp:ContentPlaceHolder
ID="TitleContent"
runat="server"
/></title>
<link
href="../../Content/Site.css"
rel="stylesheet"
type="text/css"
/>
<!-- Include the ExtJS framework
-->
<link
href="../../Scripts/ext/resources/css/ext-all.css"
rel="stylesheet"
type="text/css"
/>
<script
type="text/javascript"
src="../../Scripts/ext/ext-base.js"></script>
<script
type="text/javascript"
src="../../Scripts/ext/ext-all.js"></script>
<!-- Placeholder to include CSS and
JS files customized for each page -->
<asp:ContentPlaceHolder
ID="Scripts"
runat="server"
/>
</head>
Listing 1
Next step is to add a controller and a default view to the ASP.NET MVC solution
(see Figure 3).
Figure 3
Right-click on the Index action method of the ComboBoxController class and
select Add View option from the context menu (see Figure 4).
Figure 4
Now let's create another JS file in the Scripts directory with necessary JS code
to create a form and a combobox field inside. The code for the
comboboxfieldfom.js is shown in the Listing 2. In this file, you can see that we
are creating an Ext.FormPanel object containing our combobox item. This form
will be rendered into the comboboxform div element. This form contains a
combobox with a list of product category and a button, so when the users click
on it; then the data on the form is submitted to the server (to the URL combobox/selectcategory
using the GET method, so the request parameters are appended to the URL). We're
using the traditional submit method by setting the standardSubmit property to
true. Let's talk about the ComboBox properties. The fieldLabel property
indicates the underlying label for this field. The minListWidth property sets
the size of the list. The name property indicates the name of the underlying
request parameter. The store property references the data source where the list
of categories comes from. The displayField property is the field to be shown in
the list of the combobox. The valueField property maps a key value with the
shown value in displayField property. The mode property sets whether that the
data is remote on a server or local. The forceSelection property forces the user
to select a value from the combo, this is independent of the type of validation
allowBlank. The triggerAction property indicates the action to execute when the
trigger is clicked. The emptyText property is the text to be displayed when the
users haven't selected anything in the combo. The editable property indicates
whether you can edit on this field or not. Now we need to do a trick. If you
leave the configuration in this way, and submit the form, you can see that the
request parameter (category in this case) is always set to the value of
displayField not the valueField as it should be. After reading in the API for
Ext, I've found out that we need to set the hiddenName property to the same
value of name property, in this case is category.
Finally, we have an Ext.data.JsonStore object pointing to the productcategories
action method on the controller created previously in order to get the list of
product category. The root property references the list of categories whose
fields are Id and Name.
Ext.onReady(function()
{
var categoriesStore =
new Ext.data.JsonStore({
url: 'combobox/productcategories',
root: 'Categories',
fields: ['ProductCategoryID',
'Name']
});
var combo = new
Ext.form.ComboBox({
fieldLabel: 'Select a category',
minListWidth : 240,
store: categoriesStore,
name:'category',
hiddenName : 'category',
displayField: 'Name',
valueField: 'ProductCategoryID',
typeAhead:true,
mode:'remote',
forceSelection: true,
triggerAction: 'all',
emptyText: 'Select a category...',
editable: false
});
var form = new
Ext.FormPanel({
renderTo: 'comboboxform',
width: 400,
frame: true,
title: 'ComboBox Example',
bodyStyle: 'padding: 10px 10px 0 10px;',
labelWidth: 50,
items: combo,
url:'combobox/selectcategory',
method:'GET',
standardSubmit: true,
buttons: [{
text: 'Click here',
handler: function()
{
form.getForm().submit();
}
}]
});
});
Listing 2
Let's include the comboboxfieldfom.js (highlighted in yellow) files as well as
to create the div (highlighted in green) element (where the form will be
rendered) into the Index.aspx page created before (see Listing 3).
<%@
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>Index</h2>
<div
id="comboboxform">
</div>
</asp:Content>
<asp:Content
ID="Content3"
ContentPlaceHolderID="Scripts"
runat="server">
<script
type="text/javascript"
src="../../Scripts/comboboxfieldfom.js"></script>
</asp:Content>
Listing 3
Now let's define the business layer's objects. We're going to define the
business entity ProductCategory using Linq to SQL (see Figure 5).
Figure 5
Then add the ProductCategoryRepository class and define the
GetProductCategoryList method (see Listing 4).
using
System;
using
System.Data;
using
System.Configuration;
using
System.Linq;
using
System.Web;
using
System.Web.Security;
using
System.Web.UI;
using
System.Web.UI.HtmlControls;
using
System.Web.UI.WebControls;
using
System.Web.UI.WebControls.WebParts;
using
System.Xml.Linq;
namespace
UploadFiles_ExtJS_MvcApp.Models
{
public class
ProductCategoryRepository
{
private
ProductsDataContext _dcProducts = new
ProductsDataContext();
public IQueryable<ProductCategory>
GetProductCategoryList()
{
return from
p in this._dcProducts.ProductCategories
select p;
}
}
}
Listing 4
Now the final part is to define the ProductCategories action method to handle
HTTP GET request from the JsonStore (pointing to the URL combobox/productcategories)
to get a list of product category as well as we need the SelectCategory action
method to handle HTTP GET and HTTP POST requests in order to get the selected
category from the form (url is combobox/selectcategory). We also print the
result in the view (see Listing 5).
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Web.Mvc;
using
System.Web.Mvc.Ajax;
using
UploadFiles_ExtJS_MvcApp.Models;
namespace
UploadFiles_ExtJS_MvcApp.Controllers
{
public class
ComboBoxController :
Controller
{
//
// GET: /ComboBox/
public
ActionResult Index()
{
return View();
}
//
// GET: /ComboBox/
[AcceptVerbs(HttpVerbs.Post)]
public
ActionResult ProductCategories()
{
ProductCategoryRepository
repProductCategory = new
ProductCategoryRepository();
var results = (new
{
Categories = repProductCategory.GetProductCategoryList()
});
return Json(results);
}
[AcceptVerbs("POST",
"GET")]
public
ActionResult SelectCategory(string
category)
{
this.ViewData["CategoryID"]
= category;
return View();
}
}
}
Listing 5
Now let's run the solution. When you click on the combo box, you can see that
the component is calling to the ProductCategories action method to get a list of
product category (see Figure 6).
Figure 6
When the user selects a category and click on the button, then you can see the
URL generated (http://localhost:1032/combobox/selectcategory?category=1). Then
the SelectCategory action method is called to get the identifier of the category
and finally a view is displayed with this value (see Figure 7).
Figure 7
Conclusion
In this article, I've illustrated how to create and fill combobox controls using
ExtJS and ASP.NET MVC. Now you can apply this solution to your own business
problems.
Resources
Here are some useful related resources: