New to Telerik UI for ASP.NET AJAX? Download free 30-day trial

Client-side Databinding with WebAPI

Environment

Product Telerik WebForms Grid for ASP.NET AJAX

Description

Sometimes, one might want to bind the Grid using a Web API.

Solution

The code below shows integration of theGrid and RadClientDataSource with Web API. It shows basic databinding and batch editing sending and receiving information through Web API.

<telerik:RadNumericTextBox runat="server" ID="RadNumericTextBox1" Label="Filter by ID: " LabelWidth="65px"></telerik:RadNumericTextBox>
<telerik:RadButton AutoPostBack="false" runat="server" ID="RadButton1" Text="Filter" OnClientClicked="btnClick"></telerik:RadButton>

<telerik:RadGrid AllowFilteringByColumn="true" AllowSorting="true" AllowPaging="true" runat="server" ID="RadGrid1" AutoGenerateColumns="false"
    ClientDataSourceID="RadClientDataSource1">
    <MasterTableView EditMode="Batch" CommandItemDisplay="Top" ClientDataKeyNames="ID">
        <Columns>
            <telerik:GridBoundColumn UniqueName="ID" DataField="ID" HeaderText="ID" ReadOnly="true" />
            <telerik:GridBoundColumn UniqueName="Name" DataField="Name" HeaderText="Name" />
            <telerik:GridTemplateColumn UniqueName="Category" HeaderText="Category">
                <ClientItemTemplate>
                    <span>#=Category #</span>
                </ClientItemTemplate>
                <EditItemTemplate>
                <telerik:RadDropDownList runat="server" ID="CategoryIDDropDown">
                    <Items>
                        <telerik:DropDownListItem Text="Category1" Value="Category1" />
                        <telerik:DropDownListItem Text="Category2" Value="Category2" />
                        <telerik:DropDownListItem Text="Category3" Value="Category3" />
                        <telerik:DropDownListItem Text="Category4" Value="Category4" />
                    </Items>
                </telerik:RadDropDownList>
            </EditItemTemplate>
            </telerik:GridTemplateColumn>
            <telerik:GridBoundColumn UniqueName="Price" DataField="Price" HeaderText="Price" />
            <telerik:GridClientDeleteColumn HeaderText="Delete" ButtonType="ImageButton" HeaderStyle-Width="70px">
            </telerik:GridClientDeleteColumn>
        </Columns>
    </MasterTableView>
</telerik:RadGrid>

<telerik:RadClientDataSource runat="server" ID="RadClientDataSource1">
    <ClientEvents OnCommand="dataSourceCommand" OnRequestStart="requestStart" />
    <DataSource>
        <WebServiceDataSourceSettings>
            <Select Url="api/products" RequestType="Get" />
            <Insert Url="api/products" RequestType="Post" />
            <Update Url="api/products" RequestType="Put" />
            <Delete Url="api/products" RequestType="Delete" />
        </WebServiceDataSourceSettings>
    </DataSource>
    <Schema>
        <Model ID="ID">
            <telerik:ClientDataSourceModelField FieldName="ID" DataType="Number" />
            <telerik:ClientDataSourceModelField FieldName="Name" DataType="String" />
            <telerik:ClientDataSourceModelField FieldName="Category" DataType="String" />
            <telerik:ClientDataSourceModelField FieldName="Price" DataType="Number" />
        </Model>
    </Schema>
</telerik:RadClientDataSource>
var updatedIDs = [], deletedIDs = [];

function requestStart(sender, args) {
    var type = args.get_type();
    var transport = sender.get_dataSourceObject().options.transport;
    switch (type) {
        case "read":
            var textBox = $find('<%= RadNumericTextBox1.ClientID %>');
            var value = textBox.get_value();
            transport.read.url = "api/products/" + value;
            break;

        case "create":
            transport.create.url = "api/products/";
            break;

        case "update":
            transport.update.url = "api/products/" + updatedIDs.shift();
            break;

        case "destroy":
            transport.destroy.url = "api/products/" + deletedIDs.shift();
            break;

        default: break;
    }
}

function btnClick(sender, args) {
    var grid = $find('<%= RadGrid1.ClientID %>');
    grid.get_masterTableView().rebind();
}

function dataSourceCommand(sender, args) {
    var commandName = args.get_commandName();
    var id = args.get_commandArgument().id;
    switch (commandName) {
        case "update":
            updatedIDs.push(id);
            break;

        case "remove":
            deletedIDs.push(id);
            break;

        default: break;
    }
}
  • requestStart: This function is triggered before each request made by the RadClientDataSource. It dynamically modifies the request URLs based on the type of operation being performed (read, create, update, destroy).
  • btnClick: This function is bound to the filter button's client-side click event. It triggers a rebind of the grid's master table view, causing it to fetch data based on the entered filter ID.
  • dataSourceCommand: This function is triggered after each command executed by the RadClientDataSource. It maintains arrays (updatedIDs, deletedIDs) to track the IDs of items that have been updated or deleted.

API Config:

using System.Web.Http;

namespace ProdcutsApp
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

//  .asax page

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
    }
}

Controller:

using ProdcutsApp.Models;
using System;
using System.Collections.Generic;
using System.Net;
using System.Web.Http;

namespace ProdcutsApp.Controllers
{
    public class ProductsController : ApiController
    {
        static readonly IProductRepository repository = new ProductRepository();

        public IEnumerable<Product> GetAllProducts()
        {
            return repository.GetAll();
        }

        public IHttpActionResult GetProduct(int id)
        {
            var item = repository.Get(id);
            if (item == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            return Ok(item);
        }

        public IHttpActionResult PostProduct(Product item)
        {
            item = repository.Add(item);
            string uri = Url.Link("DefaultApi", new { id = item.ID });
            return Created(new Uri(uri), item);
        }

        public void PutProduct(int id, Product product)
        {
            product.ID = id;
            if (!repository.Update(product))
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }

        public void DeleteProduct(int id)
        {
            Product item = repository.Get(id);
            if (item == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            repository.Remove(id);
        }

    }
}

Models:

// Interface
public interface IProductRepository
{
    IEnumerable<Product> GetAll();
    Product Get(int id);
    Product Add(Product item);
    void Remove(int id);
    bool Update(Product item);
}

// Class
public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
}


// Repository
public class ProductRepository : IProductRepository
{
    private List<Product> products;
    private int currentID = 12;

    public ProductRepository()
    {
        products = new List<Product>(new Product[] 
        { 
            new Product { ID = 1, Name = "Product1", Category = "Category1", Price = 1.05M }, 
            new Product { ID = 2, Name = "Product2", Category = "Category2", Price = 3.75M }, 
            new Product { ID = 3, Name = "Product3", Category = "Category2", Price = 2.34M }, 
            new Product { ID = 4, Name = "Product4", Category = "Category3", Price = 1.63M }, 
            new Product { ID = 5, Name = "Product5", Category = "Category4", Price = 5.62M }, 
            new Product { ID = 6, Name = "Product6", Category = "Category1", Price = 7.68M }, 
            new Product { ID = 7, Name = "Product7", Category = "Category1", Price = 8.23M }, 
            new Product { ID = 8, Name = "Product8", Category = "Category2", Price = 9.44M }, 
            new Product { ID = 9, Name = "Product9", Category = "Category2", Price = 0.43M }, 
            new Product { ID = 10, Name = "Product10", Category = "Category3", Price = 2.12M }, 
            new Product { ID = 11, Name = "Product11", Category = "Category4", Price = 1.79M }, 
            new Product { ID = 12, Name = "Product12", Category = "Category4", Price = 4.22M } 
        });
    }

    public IEnumerable<Product> GetAll()
    {
        return products;
    }

    public Product Get(int id)
    {
        return products.FirstOrDefault(p => p.ID == id);
    }

    public Product Add(Product item)
    {
        if (item == null)
        {
            throw new ArgumentNullException("item");
        }
        item.ID = ++currentID;
        products.Add(item);
        return item;
    }

    public void Remove(int id)
    {
        products.RemoveAll(p => p.ID == id);
    }

    public bool Update(Product item)
    {
        if (item == null)
        {
            throw new ArgumentNullException("item");
        }
        int index = products.FindIndex(p => p.ID == item.ID);
        if (index == -1)
        {
            return false;
        }
        products.RemoveAt(index);
        products.Add(item);
        return true;
    }
}

How it Works:

  1. Filtering: Users input an ID, click the filter button, and the grid is filtered based on the entered ID.
  2. Batch Editing: Users can edit multiple rows in the grid and then save the changes in batch. The changes are sent to the Web API.
  3. Integration with Web API: The RadClientDataSource handles the communication with the Web API for CRUD operations. The requestStart function dynamically modifies URLs to reflect the current operation and data.
  4. Tracking Changes: IDs of updated and deleted items are tracked in JavaScript arrays (updatedIDs, deletedIDs). This information is later used to construct the URLs for update and delete operations.

Download the Sample

In this article