New to Telerik UI for ASP.NET MVCStart a free 30-day trial

Using Grid in InCell Batch Edit Mode and WebAPI Binding

Environment

ProductTelerik UI for ASP.NET MVC Grid
Product version2025.1.227

Description

How can I set up a batch editable Grid for WebAPI data binding?

Solution

The example relies on the following key steps:

  1. Enable the InCell batch editing of the Grid, set the type of the DataSource to WebApi(), and specify the Read, Create, Update, and Destroy endpoints.

    Razor
    @(Html.Kendo().Grid<ProductViewModel>()
        .Name("grid")
        .Columns(columns =>
        {
            ...// Define the desired columns.
            columns.Command(command => command.Destroy());
        })
        .ToolBar(tools =>
        {
            tools.Create();
            tools.Save();
        })
        .Editable(editable => editable.Mode(GridEditMode.InCell))
        .DataSource(dataSource => dataSource
            .WebApi()
            .Model(model =>
            {
                model.Id(p => p.ProductID);
            })
            .Batch(true)
            .Read(read => read.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Product", action = "get" })))
            .Create(create => create.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Product", action = "post" })))
            .Update(update => update.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Product", action = "put", id = "{0}" })))
            .Destroy(destroy => destroy.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Product", action = "delete", id = "{0}" })))
        )
    )
  2. Add a WebApi.config file in the App_Start folder:

    C#
    public class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            RouteTable.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional });
        }
    }
  3. Implement the WebAPI Controller:

    C#
    public class ProductController : ApiController
    {
        private GridEditingInCellWebApiEntities db;
    
        public ProductController()
        {
            db = new GridEditingInCellWebApiEntities();
        }
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
    
            base.Dispose(disposing);
        }
    
        // GET api/product
        public DataSourceResult Get([System.Web.Http.ModelBinding.ModelBinder(typeof(WebApiDataSourceRequestModelBinder))]DataSourceRequest request)
        {
            return db.Products.Select(p => new ProductViewModel
            {
                ProductID = p.ProductID,
                ProductName = p.ProductName,
                UnitPrice = p.UnitPrice ?? 0,
                Discontinued = p.Discontinued,
                UnitsInStock = p.UnitsInStock ?? 0
            }).ToDataSourceResult(request);
        }
        
        // POST api/product
        public HttpResponseMessage Post(ProductsRequest request)
        {
            if (ModelState.IsValid)
            {
                var products = db.AddProducts(request);
                
                var response = Request.CreateResponse(HttpStatusCode.Created, new DataSourceResult { Data =  products });
                response.Headers.Location = new Uri(Url.Link("DefaultApi", null));
                return response;
            }
            else
            {
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(error => error.ErrorMessage);
    
                return Request.CreateResponse(HttpStatusCode.BadRequest, errors);
            }
        }
    
        // PUT api/product/5
        public HttpResponseMessage Put(ProductsRequest request) // the ProductsRequest is required in order the list of Product to be correctly bind from the request
        {
            if (ModelState.IsValid)
            {
                try
                {
                    db.UpdateProducts(request); 
                }
                catch (DbUpdateConcurrencyException)
                {
                    return Request.CreateResponse(HttpStatusCode.NotFound);
                }
    
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            else
            {
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(error => error.ErrorMessage);
                return Request.CreateResponse(HttpStatusCode.BadRequest, errors);
            }
        }
    
        // DELETE api/product/5
        public HttpResponseMessage Delete(ProductsRequest request)
        {
            try
            {
                db.RemoveProducts(request);
            }
            catch (DbUpdateConcurrencyException)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
    
            return Request.CreateResponse(HttpStatusCode.OK, new DataSourceResult { Data = request.Models });
        }
    
        private bool ProductExists(int key)
        {
            return db.Products.Count(e => e.ProductID == key) > 0;
        }
    }

To review the complete example, refer to the ASP.NET MVC project on how to configure the DataSource to communicate with the WebAPI Controller when the Grid is set up for InCell batch editing.

More ASP.NET MVC Grid Resources

See Also