|Code for web.zip|
When the ASP.NET MVC framework was first released, MVC presented a brand new world for most developers, with new requirements and demands from developers and the suite of tools that they use. Because MVC uses the ASP.NET 3.5 SP1 architecture, but doesn’t leverage what Web Forms has to offer, many developers were forced to build their own components for MVC.
Lately, however, some good components are coming on the scene from a variety of sources. For instance, products such as SyncFusion offer commercial components. Other open source offerings are available, too; for example, the MVC Contrib project available on CodePlex (http://mvccontrib.codeplex.com) gives developers a set of open source UI components. I'm also working on a set of free components that will be available in the near future at http://nucleo.codeplex .com. Another choice comes from Telerik, a well-known control developer.
Telerik offers two choices for components that work in MVC. First, the components available in Telerik's Web Forms product (ASP.NET AJAX related) are compatible with MVC views (with some enhancements). Telerik has made available an MVC forum example that uses these capabilities. Second, Telerik recently released an MVC control suite that consists of four controls: Menu, PanelBar, Grid, and TabStrip. These four components use all the flavors that MVC provides. In this two-part series of articles, I’ll explore these components.
In this article, I'll explore the Grid control, which has some of the same features as the Web Forms RadGrid control. I'll also discuss the ScriptRegistrar and StylesheetRegistrar, two components Telerik created to manage style sheets and scripts.
In the second article in this series, I'll cover the remaining three controls in the Telerik MVC framework: the PanelBar, Menu, and TabStrip controls, which add functionality for MVC view developers to use in their applications.
Basics of HTML Helpers
MVC has some flavors of classic ASP; it brings back the notorious server-side tags (<% %>) used to render server-side content to the client. However, MVC is much different in its approach to classic ASP. All logic isn’t just residing within the UI; rather, it also takes shape in the form of an HTML helper. The Html property (of type HtmlHelper) that is in every view is the prime object for any server-side rendering, and it looks like the code below. Note that the method returns a string, so that it can be rendered directly to the browser.
<%= Html.TextBox(“Name”) %>
The Html property comes from System.Web.Mvc.ViewPage, which all views inherit from. Most server-side features (such as TextBox above) don’t reside within the HtmlHelper class, but instead are extension methods tagged onto the HtmlHelper class. MVC leverages everything .NET 3.5 has to offer, and this is one feature where it takes advantage of that.
The Telerik API adds a Telerik() method to the HtmlHelper class through an extension method. Each available control drills down from this method, followed by the settings for that control. For instance, look at the following code:
The PanelBar HTML helper doesn’t return the panel bar itself, but a control builder that builds the control UI in a chaining form. The Name method defines the name of the control and is required. Also, notice the Render() call, with an ending semicolon; this is the second way to render controls, through the use of a Render method that writes the HTML content to the output stream. HTML helpers that don’t return a string, but instead render their contents directly, must use a semicolon at the end.
Another common feature is the use of the Action object, and its generic forms. Actions tend to be useful; they can be used to establish startup features, or they can render custom content. Figure 1 has both, a topic we’ll explain later. The primary point of this example is to note the use of actions, and to demonstrate how you can use actions to render custom content.
Telerik uses actions to set up its UI in many different and interesting ways; for example, using it to set up the script that will handle certain events that fire within Telerik’s grid on the client. When using an Action object, the Render method must be called because of the way an Action can be used to render content.
Scripts and Styles
The Telerik control tool suite, for both Web Forms and MVC, uses style sheets and scripts to style its controls, as well as scripts to create a dynamic UI. Telerik created two components to manage these: the ScriptRegistrar and StylesheetRegistrar. These registration components handle interactions with these two content types. Before we get into these, let’s look at the content structure.
- This is a change in the way the Telerik framework handles scripts. The Telerik ASP.NET AJAX framework sets up each of the scripts and CSS files as a Web resource and uses the .NET architecture to stream it to the client. The MVC framework doesn't store these files as embedded resources. These additional files are critical to your application; it's necessary to copy the scripts and target CSS files to your individual project. The process for setting this up is as follows:
• Copy the Scripts from the Telerik.Web.Mvc project, Scripts folder, to a folder in your application (that will need to be targeted using the WebAssetDefaultSettings component).
• Copy the CSS themes from the Content folder to your application. You can copy all the themes, or copy an individual theme. Copying a single theme consists of copying the CSS file and the associated folder. For instance, for the Vista theme copy the Content/Vista folder and the telerik.vista.css file.
• Set up the default location to the scripts and CSS files for the Telerik components. The WebAssetDefaultSettings static class has two properties: ScriptFilesPath and StyleSheetFilesPath. This can be set in the Application_Startup event in the Global.asax file:
WebAssetDefaultSettings.StyleSheetFilesPath = "~/Content/Css";
Now that we have our scripts and themes set up, and the default location to these files is established, we can begin to set up the process for registering scripts/CSS files. Figure 2 sets up the script and style sheet registrar components.
These components reside, in this example, in the master page, which is ideal for your application. Scripts and stylesheets can be added to the underlying list and can be grouped together. The grouping provides additional benefits. For instance, you can cache, combine, or compress group scripts with the help of an HTTP handler. To use this HTTP handler, add the following definition to the <httpHandlers> section of the <system.web> and <system.webserver> groups:
<add verb="GET,HEAD" path="asset.axd" validate="false" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc"/>
What if you need to add additional scripts in a view or partial view that wasn’t registered at the master page level? Although Figure 2 renders the scripts in the browser, it’s possible to append to this list (the rendered registrar components appear at the end of the master page) using the ScriptRegistrar component elsewhere. The following ScriptRegistrar definition appears in the Home controller, Index view, at the bottom of the view. This adds two individual scripts to the browser, producing the result in Figure 3. Because this call to the ScriptRegistrar doesn’t render to the browser, and this call appears before the master page implementation in Figure 2, the scripts “test1.js” and “test2.js” will be appended and rendered with all the other scripts at the point ScriptRegistrar calls its Render() method.
Changes to any scripts or styles are added when the ScriptRegistrar renders its scripts to the browser via the asset.axd HTTP Handler. This produces combined or compressed scripts, which point to the handler we registered above. Additionally, each of these registrars has a default group, which is appropriately named DefaultGroup. The default group manages a group of styles with the “Default” key that you can use to register your scripts or styles. These would be the default components; thus, you wouldn’t need to create your own group at the master page level, unless you wanted to create another, separate group.
Using the RadGrid Control
In the Web Forms API, RadGrid is a powerful grid control that lets you present or manipulate your data in many ways. The MVC version may not be as powerful, but it definitely doesn't disappoint. I must admit, in some ways, the MVC grid is better because the MVC framework supports constructs that make it easier to work with your data, and it gives you more control over the resulting contents. Let’s look at the first part of our Grid definition. A grid starts with the following method calls:
The Grid method returns the instance of the Grid builder that will construct our grid, using the data source passed to it (currently, Model represents an enumerable list of the Product class, which has the following properties: Key, Name, Sku, Category, and Description). Every component is required to have a name. The name signifies the name that appears in the client, and is a required method call.
Essentially, a Grid is a tabular control made up of rows and columns. As such, the columns can be auto generated or manually set up, as shown in Figure 4. The Columns method is where the magic happens, using a column builder class to add in the columns specific to the component. Notice you can use the Action class to render custom output to the client.
The core builder component adds columns using either a lambda expression or an action. The lambda expression links up directly to the field you're adding, and can infer the name of the column from this name (this is one of the powerful features that the Expression class gives you). By adding a Title() method call, you can name this column whatever you like as an alternative. Each column object has a set of properties it can assign. For instance, you can turn on or off paging, sorting, or filtering at the column level; set up encoding for the column data; provide a format string; and establish collections for adding HTML attributes at both the header and cell level. Each feature uses a method name that’s named similar to the action you want, so it’s easy to figure out which method to call.
Each one of these methods is used in a chaining form from the object returned from the previous method call; remember that none of these options is actually set by a property. Method chaining works in both overloaded versions of the Add method on the column builder; the second overloaded option takes a reference to an action, as shown in Figure 5.
Figure 5 demonstrates two more complicated examples of using a template as the user interface for the column. The first example is not the most practical, but it does show that columns do have the ability to use other nested HTML helpers within them. Here we have a PanelBar control that’s wrapped within a Grid control. This is perfectly legal; it actually works seamlessly. The only issue with this type of control is how to convey the value that’s currently selected in a valuable way. The PanelBar is more an example to show you that this feature is possible, rather than a practical example. Notice the end of the Add method call: additional attributes can be chained, setting the title to Category, and turning off filtering and sorting.
Our next example brings in a feature that every developer knows and loves, which is the ability to edit records. Editing is a feature that isn’t available within its UI, like you see in the RadGrid Web Forms control. The grid control, instead, uses another action method in a controller to perform the work of editing. In our example, an action link redirects to another view that performs the editing. This means that the key must be passed along, and the data requeried from the database in the edit page. The downloadable code that accompanies this article has a working sample of this page, which just uses the ASP.NET session object.
The Grid control has some methods for specifying global settings (unless set differently at the column level). The first method we’ll discuss is the Pageable method. This method establishes the paging options for the control, as shown in Figure 6.
The Pageable method uses the action syntax with a chainable object, as do the rest of the methods. This action lets you establish the page size, position (defaults to bottom), and the total number of records to display (which is extracted from the model data source). The next method that looks similar to Pageable, the Scrollable method, can take a maximum height of the control, which means that the inner contents of the grid (not the header or the footer, but the grid contents) is restricted to that height, and a scrollbar appears when the content expands beyond 200 pixels.
There aren’t any filterable settings at the moment, so using the method simply enables the feature, but there are options for enabling sorting, such as whether to allow a single column to be sorted, or multiple column sorting. Last, you can alter each row that is processed by using the RowAction method. In Figure 6, the alternate rows are styled with a bold style, to differentiate the two rows.
Not only are actions at a row level supported, cell actions also are available. The code in Figure 7 looks for SKU codes starting with a specific set of digits, and if those values match, it colors the cell red (and only that cell).
Cell actions give you the ability to highlight or change the look of a specific style for a specific column, similar to the DayRender event of the server Calendar control in the ASP.NET Web Forms framework (except this approach requires no event calls).
Using AJAX in the Grid
RadGrid also supports using AJAX. At its core, using AJAX is very simple. All that is required is to hook up the RadGrid with a controller’s action method that implements some specific requirements. This makes using AJAX super easy, and a really powerful feature. Figure 8 shows how to use this new AJAX feature.
The Telerik grid does support paging, sorting, and filtering in pure AJAX form. There is no additional setup, but realize that Telerik uses the entire data source to sort out what the current page is (paging information is stored as a query string variable), what the current sort is, and what filters are applied. The controller that would serve the data to the grid is shown in Figure 9.
The two requirements for adding AJAX support is to define a GridActionAttribute for the method returning a data source to a Grid in an AJAX call, and to return a GridModel object with the underlying data. The GridModel object takes two parameters: the actual data source and the total number of records. At a minimum, the IGridModel interface identifies the object that stores the model for the grid, but the GridModel and GridModel<> classes support storing the database data for you too.
The Grid control uses this action method to rebind itself on every page, filter, or sort. With one method, you get complete AJAX functionality in the grid, without actually seeing the control make a post to the action method. It works very well, but note that this requires binding your data for these interactions.
Making JQuery Get Calls
JQuery asynchronous postbacks are awesome; it’s not that JQuery has invented something new to do this, but the framework makes this feat so easy. Simply use the $.get method, supply the URL to retrieve, provide a callback with a parameter to that callback for the response, and you're all set. Let’s walk through an example of posting back a grid. Figures 10 and 11 contain our view page and associated partial view.
From a UI perspective, this example has two main pieces. The first piece (Figure 10) is the view that contains a marker for where our grid will go. This grid will be stored as a child to the gridplaceholder DIV tag, and is loaded upon clicking the action link. The action link is responsible for querying the data and binding the grid, but there is more that goes on here.
The action link points to a URL for /Grid/GetGrid, and will postback to the action method and rerender the view. Our goal is to instead use JQuery to make this re-rendering happen on the client, and give the user the impression that the application doesn’t post back to the server. Every time some link causes a postback, JQuery intercepts the click, makes a GET call to the server to retrieve the data, and updates the interface by replacing the old HTML content with the new content.
Clicking the link will now respond solely to the JQuery click event handler, which then triggers a GET operation to the previous HREF location. In Figure 10, the link previously pointed to the URL “/Grid/GetGrid”; the GET operation now queries data from this URL and streams the response to the browser. The response from the server (our action method in the controller—Figure 11) is responsible for returning the HTML for our partial view, which also has a Telerik Grid. You must take the following additional steps for this to work:
- If the Telerik scripts for the Grid component are not already registered (when the grid didn’t previously exist), you must manually register these scripts using the ScriptRegistrar.
It appeared to me during testing of this feature that the JQuery get approach illustrated here and the approach used by Telerik’s AJAX features are different. If you set up the Ajax options using the Ajax method for the Telerik grid, it will force a postback because of how the Telerik Grid works internally (using the query string to store its parameters and such). This required me to change my example because the JQuery setup works by client-side clicks making calls to server-side action methods, and so I sought to find a way to make this work.
The code in Figure 13 is my solution; it contains a helper script that attaches to a Grid component, and replaces the hyperlinks that post back to the server with a JQuery click event. The trick was to find all the hyperlinks with the t-link class. All the postback operations I found so far (the ones I was concerned with) turned out to be defined as hyperlinks, with the t-link class, that could be easily targeted using the script in Figure 13. Note that this method must be called every time the UI is refreshed from the server. Now the Grid’s link only post to the server using JQuery GET operations, and the entire grid is replaced with this new response from the server.
MVC is a great product and Telerik MVC components make it even better. In this was part one of a two-part series, I explored one of four custom controls: the Grid. I also looked at the Script and Stylesheet registrars, which provide an effective way to manage these assets within your application. The second article in this series will cover the PanelBar, TabStrip, and Menu controls in detail.