Sunday, November 29, 2009 10:16 PM Kazi Manzur Rashid

Telerik ASP.NET MVC Grid Preview (Take 2)

In the last post, I have shown you how you can extend the ASP.NET MVC2 templates and some of the  issues of the current beta. In this post, I will show you how we are taking advantages of the new DisplayFor/EditorFor in our MVC Grid. One of the benefit of using those statements in our grid is, you can host any kind of component in an individual cell, obviously the first two components will be the DateTimePicker and NumericTextBox that we are going to include in our next release.

One of the most requested feature since our last release is the inline editing support in the grid. In this, In this post, I will show you the inline editing of the product of my last post, but this time with our Grid. The Grid should support both server and ajax editing, but in this example we will only discuss the server side mode.

So lets see the grid first:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<GridModel<Product>>" %>

<% Html.Telerik()
       .Grid(Model)
       .Name("products")
       .Columns(columns =>
        {
           columns.Add((p, commands) =>
           {
               commands.Edit()
                       .Action<ProductController>(c => c.Edit(p.Id));

               commands.Delete()
                       .Action<ProductController>(c => c.Delete(p.Id));

           });

           columns.Add(p => p.Name);
           columns.Add(p => p.Category);
           columns.Add(p => p.Price);
        })
       .Filterable()
       .Sortable()
       .Scrollable()
       .Pageable()
       .Render(); %>

As you can see that we are introducing a new kind of column - GridCommandColumn(line 6-16). This column type can contain multiple actions, for example in the above code snippet we have both Edit and Delete action, you can also add custom actions, in that case you have to specify the name as well.

Now, let us take a look at the controller code:

[HandleError, PopulateCategories]
public class ProductController : Controller
{
    private readonly IDatabase database;

    public ProductController() : this(Database.Create())
    {
    }

    public ProductController(IDatabase database)
    {
        this.database = database;
    }

    [GridAction(GridName = "products")]
    public ActionResult Index(GridCommand command)
    {
        return View(database.Products.ToGridModel(command));
    }

    [HttpPost, GridAction(GridName = "products")]
    public ActionResult Edit(int id)
    {
        Product product = database.Products.FirstOrDefault(p => p.Id == id);

        // Since we are working with in memory version, we are clonnening it,
        // in real life this would not be necessary.

        Product clonned = new Product
                              {
                                  Id = product.Id,
                                  Name = product.Name,
                                  Category = new Category { Id = product.Category.Id, Name = product.Category.Name},
                                  Price = product.Price
                              };

        if (TryUpdateModel(clonned, null, null, new[] { "Id" }))
        {
            product = clonned;
            return RedirectToAction("Index");
        }

        return View("Index", database.Products.ToGridModel(command));
    }

    [HttpPost, GridAction(GridName = "products")]
    public ActionResult Delete(int id)
    {
        Product product = database.Products.FirstOrDefault(p => p.Id == id);

        database.Products.Remove(product);

        return RedirectToAction("Index");
    }
}

When your run the above code and navigate to product/index the following view will appear:

InlineGridEditing

When clicking the Edit Button, it will enter into the edit mode, the edit button will be replaced with two new buttons Update and Cancel and the Delete button will be become hidden. The text of these button are completely configurable, you can set those when setting up the Grid. The following is the screenshot when the Grid enters into edit mode.

InlineGridEditingMode

 

When the validation fails:

InlineGridValidation

The Grid also have the out of the box support of PRG (Post/Redirect/Get) pattern, to activate it you have set few properties of the GridAction filter. The following is the complete code of the Product Controller when PRG is enabled.

[HandleError, PopulateCategories]
public class ProductController : Controller
{
    private readonly IDatabase database;

    public ProductController() : this(Database.Create())
    {
    }

    public ProductController(IDatabase database)
    {
        this.database = database;
    }

    [GridAction(GridName = "products", ImportViewDataFromTempData = true)]
    public ActionResult Index(GridCommand command)
    {
        return View(database.Products.ToGridModel(command));
    }

    [HttpPost, GridAction(GridName = "products", ExportViewDataToTempData = true)]
    public ActionResult Edit(int id)
    {
        Product product = database.Products.FirstOrDefault(p => p.Id == id);

        // Since we are working with in memory version, we are clonnening it,
        // in real life this would not be necessary.

        Product clonned = new Product
                              {
                                  Id = product.Id,
                                  Name = product.Name,
                                  Category = new Category { Id = product.Category.Id, Name = product.Category.Name},
                                  Price = product.Price
                              };

        if (TryUpdateModel(clonned, null, null, new[] { "Id" }))
        {
            product = clonned;
        }

        return this.RedirectToPreviousOr(() => RedirectToAction("Index"));
    }

    [HttpPost, GridAction(GridName = "products", ExportViewDataToTempData = true)]
    public ActionResult Delete(int id)
    {
        Product product = database.Products.FirstOrDefault(p => p.Id == id);

        database.Products.Remove(product);

        return this.RedirectToPreviousOr(() => RedirectToAction("Index"));
    }
}

As you can see that the Edit and Delete action are copying the ViewData to TempData and the Index action is copying the ViewData back from the TempData so that validation messages get intact between the redirects. There one more thing in the above code that we are using RedirectToPreviousOr, this is just a extension method which checks whether the UrlReferrer is set, if set it redirect to that url or execute the provided action.

public static ActionResult RedirectToPreviousOr(this ControllerBase controller, Func<ActionResult> action)
{
    HttpContextBase httpContext = controller.ControllerContext.HttpContext;

    string previousUrl = (httpContext.Request.UrlReferrer != null) ? httpContext.Request.UrlReferrer.ToString() : null;

    return !string.IsNullOrEmpty(previousUrl) ? new RedirectResult(previousUrl) : action();
}

Now, lets check two other screenshots which contains multiple data types:

multiv

multie

What do you think, comments and suggestions are greatly appreciated.

Shout it
Filed under: , , , , ,

Comments

# re: Telerik ASP.NET MVC Grid Preview (Take 2)

Sunday, November 29, 2009 7:12 PM by tomas

hi kazi,

when will you pick upp the shrinkr series again? i want the rest of the story! =)

// tomas

# re: Telerik ASP.NET MVC Grid Preview (Take 2)

Monday, November 30, 2009 1:00 PM by kim

Does this work in MVC 2 Beta 2 ?

# re: Telerik ASP.NET MVC Grid Preview (Take 2)

Monday, November 30, 2009 1:58 PM by Kazi Manzur Rashid

@tomas : I know, sorry to keep you guys waiting.

# re: Telerik ASP.NET MVC Grid Preview (Take 2)

Monday, November 30, 2009 2:01 PM by Kazi Manzur Rashid

@kim: This post is a preview of our upcoming release, The current version does not work with MVC2 as MVC2 has breaking changes with v1.0

From our next release we are planning to include two separate binaris for each version.