Reloading a Razor WebGrid after Ajax calls using a partial view
If you are using Razor and MVC you probably make some use of the built in controls in System.Web.Helpers. WebGrid, located in the Helpers assembly, was created for WebMatrix’s Razor Web Pages and landed itself nicely to Razor views in MVC.
WebGrid, much like ASP.NET’s ListView control, is designed to display a data driven HTML table on the screen. It has support for paging, sorting and column customization.
In this article, we will be taking a quick look at loading a WebGrid with data, both as a part of a page and as a standalone AJAX call. AJAX calls can occur after a page load or sort change and as a part of the interaction of a user with the data on the grid.
Let’s begin by making a view. The view will contain the body of the page rendered in the @RenderBody() section of the main layout. In our case the grid will reside in the index page and the index view will simply look like this:
@model IEnumerable<Deployments.Models.Deployment> @{ ViewBag.Title = "Deployments"; } <h2>@ViewBag.Message</h2> <p> @Html.ActionLink("Request Deployment", "CreateDeployment") </p> @Html.Partial("DeploymentList", Model)
The Model is the data we wish to display and Html.Partial adds a partial view along with our grid.
We will take a look at the data later. First let’s display our grid. The grid resided in a partial view named DeploymentList:
@model IEnumerable<Deployments.Models.Deployment> @{ var grid = new WebGrid(null, rowsPerPage: ViewBag.PageSize, ajaxUpdateContainerId: "deploymentsGrid", canSort: false); grid.Bind(Model, rowCount: ViewBag.TotalRecords, autoSortAndPage: false); }
A key part of the grid is ajaxUpdateContainerId. It signals the grid what container to update after an AJAX call. “deploymentsGrid” is a div surrounding the partial view and containing the Grid and any helper functions that will get updated in AJAX calls.
Now after we declared the grid we can look at the controller. The grid sends some query string parameters for paging and sorting by default. Since our grid has pagination enabled, our controller should be able to deal with an optional page parameter.
private DeploymentsEntities context = new DeploymentsEntities(); private int pageSize = 10; public ActionResult Index(int? page) { ViewBag.Message = "Deployments"; ViewBag.PageSize = pageSize; ViewBag.TotalRecords = context.Deployments.Count(d => d.Active == true); var model = context.Deployments .Include("Website") .Include("Environment") .OrderByDescending(d => d.DeploymentID) .Where(d => d.Active == true) .Skip(((page.HasValue ? page.Value : 1) - 1) * pageSize) .Take(pageSize); if (Request.IsAjaxRequest()) return PartialView("DeploymentList", model); else return View(model); }
As you can see, when our controller is invoked by an AJAX request, we do not need to return the entire index view. We can simply return the partial view containing the grid. Retuning a partial view will make sure only the HTML we need to update is actually being generated.
This is the rest of the grid view:
<div id="deploymentsGrid"> @grid.GetHtml( columns: grid.Columns( grid.Column("Website.WebsiteName", header: "Website"), grid.Column("Environment.EnvironmentName", header: "From"), grid.Column("Environment1.EnvironmentName", header: "To"), grid.Column("RequestedBy", header: "Requested By"), grid.Column("RequestedDate", header: "Requested Time"), grid.Column("ExecutedBy", header: "Executed By"), grid.Column("ExecutedDate", header: "Executed Time"), grid.Column("WebsiteSync", header: "Website", format: (item) => (item.WebsiteSync) ? Html.Raw("<img src=‘/images/active.png‘ />") : Html.Raw("<img src=‘/images/inactive.png‘ />")), grid.Column("DatabaseSync", header: "Database", format: (item) => (item.DatabaseSync) ? Html.Raw("<img src=‘/images/active.png‘ />") : Html.Raw("<img src=‘/images/inactive.png‘ />")), grid.Column("Comments", header: "Comments"), grid.Column("", header: "Done", format: (item) => (string.IsNullOrWhiteSpace(item.ExecutedBy)) ? @Ajax.ActionLink("Done", "Done", new { id = item.DeploymentID }, new AjaxOptions() { Confirm = "Did you check your work?", HttpMethod = "Get", OnSuccess = "updateGrid()" }) : Html.Raw("<a href=‘" + item.Environment1.WebsiteURL + "‘ target=‘_new‘>View</a>")) ) ) <script type="text/javascript"> function updateGrid() { @Html.Raw(HttpUtility.HtmlDecode(grid.GetContainerUpdateScript("/?page=" + (grid.PageIndex + 1)).ToString())) } </script> </div>
If you read through the code, you probably noticed the @Ajax.ActionLink “Done”. This is a simple GET call along with a parameter containing the ID of the record we wish to update. The interesting part about it is the OnSuccess call. This is a JavaScript call that is made when the ActionLink returns successfully.
Since our update operation succeeded, we will go ahead and update the grid:
@Html.Raw(HttpUtility.HtmlDecode(grid.GetContainerUpdateScript("/?page=" + (grid.PageIndex + 1)).ToString()))
GetContainerUpdateScript is built into the grid. If you view the source of the page, you will probably find it in the JavaScript onClick events of your pager. We are simply calling it again, along with the current grid page in order to update the container div.
As you can tell, WebGrid has some great web 2.0 functionality out of the box and can help you speed up Razor development.