Local data in Syncfusion ASP.NET MVC Grid

5 Mar 202524 minutes to read

The Syncfusion ASP.NET MVC Grid offers a straightforward way to bind local data, such as arrays or JSON objects, to the Grid. This feature allows you to display and manipulate data within the Grid without the need for external server calls, making it particularly useful for scenarios where you’re working with static or locally stored data.

To achieve this, you can assign a IEnumerable object to the DataSource property. Additionally, you have an option to provide the local data source using an instance of the DataManager.

The following example demonstrates how to utilize the local data binding feature in the Grid:

@Html.EJS().Grid("grid").DataSource((IEnumerable<object>)ViewBag.DataSource).Height("348px").Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("150").Add();
    col.Field("OrderDate").HeaderText("Order Date").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("130").Format("yMd").Add();
    col.Field("Freight").HeaderText("Freight").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Format("C2").Add();
    col.Field("ShipCountry").HeaderText("Ship Country").Width("150").Add();
}).AllowPaging().Render()
public ActionResult Index()
{
    ViewBag.DataSource = OrderDetails.GetAllRecords();
    return View();
}

Local data

Data binding with SignalR

The Syncfusion ASP.NET MVC Grid provides support for real-time data binding using SignalR, allowing you to update the Grid automatically as data changes on the server-side. This feature is particularly useful for applications requiring live updates and synchronization across multiple clients.

To achieve real-time data binding with SignalR in your Grid, follow the steps below:

Step 1: To create a new ASP.NET Web Application(.NET Framework) project named SignalR, follow these steps:

  • Open Visual Studio.
  • Select “Create a new project”
  • Choose ASP.NET Web Application (.NET Framework) project template.
  • Name the project SignalR.
  • Click “Create”

Step 2: Create a simple Grid by following the Getting Started documentation link.

Step 3: Create a controller on the server-side to manage data operations such as fetching, updating, inserting, and deleting records. You can create a HomeController.cs file under the Controllers folder. Add the following code to define methods for sending data updates to clients:

using Syncfusion.EJ2.Base;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using SignalR.Models;

namespace SignalR.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult UrlDatasource(DataManagerRequest dm)
        {
            IEnumerable DataSource = OrdersDetails.GetAllRecords();
            DataOperations operation = new DataOperations();
            List<string> str = new List<string>();
            if (dm.Search != null && dm.Search.Count > 0)
            {
                DataSource = operation.PerformSearching(DataSource, dm.Search);
            }
            if (dm.Sorted != null && dm.Sorted.Count > 0)
            {
                DataSource = operation.PerformSorting(DataSource, dm.Sorted);
            }
            if (dm.Where != null && dm.Where.Count > 0)
            {
                DataSource = operation.PerformFiltering(DataSource, dm.Where, dm.Where[0].Operator);
            }
            int count = DataSource.Cast<OrdersDetails>().Count();
            if (dm.Skip != 0)
            {
                DataSource = operation.PerformSkip(DataSource, dm.Skip);
            }
            if (dm.Take != 0)
            {
                DataSource = operation.PerformTake(DataSource, dm.Take);
            }
            return dm.RequiresCounts ? Json(new { result = DataSource, count = count }) : Json(DataSource);
        }

        //update the record.
        public void Update(CRUDModel<OrdersDetails> updatedRecord)
        {
            // Retrieve updated order.
            var updatedOrder = updatedRecord.value;
            if (updatedOrder != null)
            {
                // Find existing record.
                var data = OrdersDetails.GetAllRecords().FirstOrDefault(or => or.OrderID == updatedOrder.OrderID);
                if (data != null)
                {
                    // Update existing record.
                    data.OrderID = updatedOrder.OrderID;
                    data.Freight = updatedOrder.Freight;
                    data.CustomerID = updatedOrder.CustomerID;
                    data.ShipCity = updatedOrder.ShipCity;
                    data.ShipCountry = updatedOrder.ShipCountry;
                }
            }
        }

        //Insert the record.
        public void Insert(CRUDModel<OrdersDetails> newRecord)
        {
            // Check if new record is not null.
            if (newRecord.value != null)
            {
                // Insert new record.
                OrdersDetails.GetAllRecords().Insert(0, newRecord.value);
            }
        }

        //Delete the record.
        public void Delete(CRUDModel<OrdersDetails> deletedRecord)
        {
            int orderId = int.Parse(deletedRecord.key.ToString()); // Get key value from the deletedRecord.
            var data = OrdersDetails.GetAllRecords().FirstOrDefault(orderData => orderData.OrderID == orderId);
            if (data != null)
            {
                // Remove the record from the data collection.
                OrdersDetails.GetAllRecords().Remove(data);
            }
        }

        public class CRUDModel<T> where T : class
        {
            public string action { get; set; }
            public string keyColumn { get; set; }
            public object key { get; set; }
            public T value { get; set; }
            public List<T> added { get; set; }
            public List<T> changed { get; set; }
            public List<T> deleted { get; set; }
            public IDictionary<string, object> @params { get; set; }
        }
    }
}

Step 4: Create a model class named OrdersDetails.cs under the Models folder in the server-side project to represent the order data. Add the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SignalR.Models
{
    public class OrdersDetails
    {
        public static List<OrdersDetails> order = new List<OrdersDetails>();
        public OrdersDetails()
        {

        }

        public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress)
        {
            this.OrderID = OrderID;
            this.CustomerID = CustomerId;
            this.EmployeeID = EmployeeId;
            this.Freight = Freight;
            this.ShipCity = ShipCity;
            this.Verified = Verified;
            this.OrderDate = OrderDate;
            this.ShipName = ShipName;
            this.ShipCountry = ShipCountry;
            this.ShippedDate = ShippedDate;
            this.ShipAddress = ShipAddress;
        }

        public static List<OrdersDetails> GetAllRecords()
        {
            if (order.Count() == 0)
            {
                int code = 10000;
                for (int i = 1; i < 5; i++)
                {
                    order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6"));
                    order.Add(new OrdersDetails(code + 2, "ANATR", i + 2, 3.3 * i, true, new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", new DateTime(1996, 9, 11), "Avda. Azteca 123"));
                    order.Add(new OrdersDetails(code + 3, "ANTON", i + 1, 4.3 * i, true, new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo"));
                    order.Add(new OrdersDetails(code + 4, "BLONP", i + 3, 5.3 * i, false, new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", new DateTime(1996, 12, 30), "Magazinweg 7"));
                    order.Add(new OrdersDetails(code + 5, "BOLID", i + 4, 6.3 * i, true, new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", new DateTime(1997, 12, 3), "1029 - 12th Ave. S."));
                    code += 5;
                }
            }
            return order;
        }
        public int? OrderID { get; set; }
        public string CustomerID { get; set; }
        public int? EmployeeID { get; set; }
        public double? Freight { get; set; }
        public string ShipCity { get; set; }
        public bool? Verified { get; set; }
        public DateTime OrderDate { get; set; }
        public string ShipName { get; set; }
        public string ShipCountry { get; set; }
        public DateTime ShippedDate { get; set; }
        public string ShipAddress { get; set; }
    }

}

Step 5: In your client-side code, establish a connection to the SignalR hub and configure Grid data binding in the Views/Home/Index.cshtml file:

@Html.EJS().Grid("grid").Height("248px").DataSource(ds => ds.Url(@Url.Action("UrlDatasource", "Home")).InsertUrl(@Url.Action("Insert", "Home")).UpdateUrl("/Home/Update").RemoveUrl(@Url.Action("Delete", "Home")).Adaptor("UrlAdaptor")).Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").Width("120").IsPrimaryKey(true).TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("170").Add();
    col.Field("ShipCity").HeaderText("Ship City").Width("120").Add();
    col.Field("ShipCountry").HeaderText("Ship Country").Width("120").Add();
}).AllowPaging().Created("onCreated").EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true); }).Toolbar(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" }).ActionComplete("actionComplete").Render()
@section scripts {
     <script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
     <!--Reference the autogenerated SignalR hub script. -->
     <script src="~/signalr/hubs"></script>
     <!--SignalR script to update the chat page and send messages.-->
     <script>
       // Reference the auto-generated proxy for the hub.
       var chat = $.connection.chatHub;
       function onCreated() {
           // Hub call back function.
           chat.client.addNewMessageToPage = function (name, message) {
             // Grid is refreshed.
             var gridObject = document.getElementById('grid').ej2_instances[0];
             gridObject.refresh();
           };
           // Start the connection. 
           $.connection.hub.start().done(function () {
              console.log("SignalR connection established successfully");
              // Call the Send method on the hub.
              chat.server.send("SendMessage", "refreshPages").catch((err) => {
                console.error("Error sending data:", err.toString());
              });
           }).catch((err) => {
                console.error("Error establishing SignalR connection:", err.toString());
            });
       }

       function actionComplete(args) {
          if (args.requestType === "save" || args.requestType === "delete") {
             chat.server.send("SendMessage", "refreshPages").catch((err) => {
                console.error(err.toString());
             });
          }
       }

     </script>
}

Step 6: Create a SignalR Hub on the Server Side:

Create a SignalR hub to manage communication between clients and the server. To do this:

  1. Create the Hubs folder.

2.Right-click on the Hubs folder and select Add > New Item.

  1. Select SignalR Hub Class (v2) and name the file ChatHub.cs, then click Add. This step adds a set of script files and assembly references that support SignalR.

  2. Add the following code to define methods for sending data updates to clients:

using Microsoft.AspNet.SignalR;

namespace SignalR.Hubs
{
    public class ChatHub : Hub
    {
        public void Send(string name, string message)
        {
            // Call the addNewMessageToPage method to update clients.
            Clients.All.addNewMessageToPage(name, message);
        }
    }
}

Step 7: Create the Startup.cs file to configure the SignalR server and route requests to the SignalR hub. Add the following code:

using Microsoft.Owin;
using Owin;
using System;
using System.Threading.Tasks;

[assembly: OwinStartup(typeof(SignalR.Startup))]

namespace SignalR
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

The following screenshot represents the addition, editing, and deletion operations performed, reflecting changes across all client sides:

Data binding with SignalR

Binding data from excel file

The Syncfusion ASP.NET MVC Grid allows you to import data from Excel files into your web application for display and manipulation within the Grid. This feature streamlines the process of transferring Excel data to a web-based environment. This can be achieved by using Uploader Change event.

To import excel data in to Grid, you can follow these steps:

  1. Import excel file using Uploader.
  2. Parse the excel file data using XLSX library.
  3. Bind the JSON to the Grid.

The following example demonstrates how to import Excel data into the Grid by utilizing the Uploader Change event along with the XLSX library:

<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<div>
    <label style="font-weight: bold">Browse excel file to load and return grid</label>
    <div style="padding:20px 0 0 0">
    @Html.EJS().Uploader("upload").Removing("onRemove").Change("onSuccess").AsyncSettings(new Syncfusion.EJ2.Inputs.UploaderAsyncSettings { SaveUrl = "https://services.syncfusion.com/aspnet/production/api/FileUploader/Save", RemoveUrl = "https://services.syncfusion.com/aspnet/production/api/FileUploader/Remove" }).Render()
    </div>
</div>
@Html.EJS().Grid("grid").AllowPaging().Render()
@Html.EJS().Dialog("dialog").ShowCloseIcon(true).Header("Alert").Visible(false).Content("Invalid JSON").Width("250px").Render()
<script>
    function onRemove(args) {
        var grid = document.getElementById("grid").ej2_instances[0];
        grid.dataSource = [''];
        grid.columns = [];
    }

    function onSuccess(args) {
        var files = args.file;
        if (files) {
           parseExcel(files[0]);
        }
    }
    
    function parseExcel(file) {
        var grid = document.getElementById("grid").ej2_instances[0];
        var dialog = document.getElementById("dialog").ej2_instances[0];
        if (file && file.type == 'xlsx') {
            var reader = new FileReader();
            reader.onload = (e) => {
                var data = (e.target).result;
                var workbook = XLSX.read(data, { type: 'array' });
                workbook.SheetNames.forEach((sheetName) => {
                var XL_row_object = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName]);
                if (Array.isArray(XL_row_object) && XL_row_object.length > 0) {
                    grid.dataSource = XL_row_object;
                } else {
                    dialog.content = "Invalid JSON";
                    dialog.show();
                }
                });
            };
            reader.readAsArrayBuffer(file.rawFile);
        } else {
            dialog.content = 'Please upload only .xlsx format';
            dialog.show();
        }
    }
</script>
public ActionResult Index()
{
    return View();
}

Local data

Binding data and performing CRUD actions via Fetch request

The Syncfusion ASP.NET MVC Grid provides a seamless way to bind data from external sources using Fetch requests, facilitating CRUD (Create, Read, Update, Delete) operations with data retrieved from a server. This feature is particularly valuable for sending data to a server for database updates and asynchronously retrieving data without refreshing the entire web page.

To achieve data binding and perform CRUD actions using Fetch requests in the Grid, follow these steps:

Step 1: To create a new ASP.NET Web Application(.NET Framework) project named FetchRequest, follow these steps:

  • Open Visual Studio.
  • Select “Create a new project”
  • Choose ASP.NET Web Application(.NET Framework) project template.
  • Name the project FetchRequest.
  • Click “Create”

Step 2: Create a simple Grid by following the Getting Started documentation link.

Step 3: In the Views/Home/Index.cshtml file, follow the steps below.

A. To bind data from an external Fetch request, utilize the DataSource property of the Grid. Fetch data from the server and provide it to the DataSource property using the onSuccess event of the Fetch request.

B. To perform CRUD actions, leverage the ActionBegin event. You can cancel the default CRUD operations by utilizing the cancel argument provided by this event. This allows you to dynamically call your server-side method using Fetch, along with the relevant data received from the ActionBegin event, to update your server data accordingly.

C. In the Fetch success event, you have the flexibility to utilize the Grid endEdit and deleteRecord methods to handle the addition, editing, and deletion of corresponding data in the Grid. However, invoking these methods triggers the ActionBegin event once again to save the changes in the Grid. To prevent this behavior and maintain control over the execution flow, you can employ a flag variable and manage it within the ActionComplete and Fetch failure events: The following code snippet demonstrates this approach:

<div style="padding: 10px 0px 20px 0px">
   @Html.EJS().Button("sample").CssClass("e-success").Content("Bind data via Fetch").Render()
</div>
@Html.EJS().Grid("grid").Height("348px").Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").IsPrimaryKey(true).TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Add();
    col.Field("CustomerID").HeaderText("Customer ID").Width("150").Add();
    col.Field("ShipCity").HeaderText("Ship City").Width("150").Add();
    col.Field("ShipCountry").HeaderText("Ship Country").Width("150").Add();
}).ActionBegin("actionBegin").ActionComplete("actionComplete").Toolbar(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" }).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true).Mode(Syncfusion.EJ2.Grids.EditMode.Normal); }).AllowPaging().Render()
<script>
   let flag = false;
   document.getElementById('sample').onclick = function () {
       var grid = document.getElementById("grid").ej2_instances[0];
       const fetchRequest = new ej.base.Fetch("/Home/Getdata", 'POST'); 
        fetchRequest.send();
        fetchRequest.onSuccess = (data) => {
            grid.dataSource = data;
        };
    };

    function actionComplete(e) {
        if (e.requestType === 'save' || e.requestType === 'delete') {
            flag = false;
        }
    }

    function actionBegin(e) {
        var grid = document.getElementById("grid").ej2_instances[0];
        if (!flag) {
            if (e.requestType == 'save' && (e.action == 'add')) {
                var editedData = e.data;
                e.cancel = true;
                var fetchRequest = new ej.base.Fetch({
                    url: '/Home/Insert',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify({ value: editedData })
                });
                fetchRequest.onSuccess = () => {
                    flag = true;
                    grid.endEdit();
                };
                fetchRequest.onFailure = () => {
                    flag = false;
                };
                fetchRequest.send();
            }
            if (e.requestType == 'save' && (e.action == "edit")) {
                var editedData = e.data;
                e.cancel = true;
                var fetchRequest = new ej.base.Fetch({
                    url: '/Home/Update',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify({ value: editedData })
                }); 
                fetchRequest.onSuccess = () => {
                    flag = true;
                    grid.endEdit();
                };
                fetchRequest.onFailure = () => {
                    flag = false;
                };
                fetchRequest.send();
            }
            if (e.requestType == 'delete') {
                var editedData = e.data;
                e.cancel = true;
                var fetchRequest = new ej.base.Fetch({
                    url: '/Home/Delete',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify({ key: editedData[0][grid.getPrimaryKeyFieldNames()[0]] })
                });
                fetchRequest.onSuccess = () => {
                    flag = true;
                    grid.deleteRecord();
                };
                fetchRequest.onFailure = () => {
                    flag = false;
                };
                fetchRequest.send();
            }
        }
    }
</script>

Step 7: In the HomeController.cs file under the Controllers folder, there is a method named GetData within the HomeController that provides the data source for the Grid. When the button is clicked, a Fetch request is sent to retrieve the data from the server and bind it to the Grid. Additionally, implement server-side logic to perform add, edit, and delete operations. Add the following code:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using FetchRequest.Models;
namespace FetchRequest.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult GetData()
        {
            IEnumerable DataSource = OrdersDetails.GetAllRecords();
            return Json(DataSource);
        }

        public ActionResult Update(OrdersDetails value)
        {
            var ord = value;
            OrdersDetails val = OrdersDetails.GetAllRecords().Where(or => or.OrderID == ord.OrderID).FirstOrDefault();
            val.OrderID = ord.OrderID;
            val.EmployeeID = ord.EmployeeID;
            val.CustomerID = ord.CustomerID;
            val.Freight = ord.Freight;
            val.OrderDate = ord.OrderDate;
            val.ShipCity = ord.ShipCity;
            val.ShipCountry = ord.ShipCountry;
            return Json(value);
        }

        //Insert the record.
        public ActionResult Insert(OrdersDetails value)
        {
            OrdersDetails.GetAllRecords().Insert(0, value);
            return Json(value);
        }

        //Delete the record.
        public ActionResult Delete(int key)
        {
            OrdersDetails.GetAllRecords().Remove(OrdersDetails.GetAllRecords().Where(or => or.OrderID == key).FirstOrDefault());
            var data = OrdersDetails.GetAllRecords();
            return Json(data);
        }
    }
}

Step 5: Create a model class named OrdersDetails.cs under the Models folder in the server-side project to represent the order data. Add the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace FetchRequest.Models
{
    public class OrdersDetails
    {
        public OrdersDetails() { }
        public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress)
        {
            this.OrderID = OrderID;
            this.CustomerID = CustomerId;
            this.EmployeeID = EmployeeId;
            this.Freight = Freight;
            this.ShipCity = ShipCity;
            this.Verified = Verified;
            this.OrderDate = OrderDate;
            this.ShipName = ShipName;
            this.ShipCountry = ShipCountry;
            this.ShippedDate = ShippedDate;
            this.ShipAddress = ShipAddress;
        }

        public static List<OrdersDetails> GetAllRecords()
        {
            List<OrdersDetails> order = new List<OrdersDetails>();
            int code = 10000;
            for (int i = 1; i < 10; i++)
            {
                order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6"));
                order.Add(new OrdersDetails(code + 2, "ANATR", i + 2, 3.3 * i, true, new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", new DateTime(1996, 9, 11), "Avda. Azteca 123"));
                order.Add(new OrdersDetails(code + 3, "ANTON", i + 1, 4.3 * i, true, new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo"));
                order.Add(new OrdersDetails(code + 4, "BLONP", i + 3, 5.3 * i, false, new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", new DateTime(1996, 12, 30), "Magazinweg 7"));
                order.Add(new OrdersDetails(code + 5, "BOLID", i + 4, 6.3 * i, true, new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", new DateTime(1997, 12, 3), "1029 - 12th Ave. S."));
                code += 5;
            }
            return order;
        }
        public int? OrderID { get; set; }
        public string CustomerID { get; set; }
        public int? EmployeeID { get; set; }
        public double? Freight { get; set; }
        public string ShipCity { get; set; }
        public bool Verified { get; set; }
        public DateTime OrderDate { get; set; }
        public string ShipName { get; set; }
        public string ShipCountry { get; set; }
        public DateTime? ShippedDate { get; set; }
        public string ShipAddress { get; set; }
    }
}

The following screenshot represents loading data when the button is clicked and CRUD operations are performed:

Binding data and performing CRUD actions via Fetch request

Display the loading indicator with local data

The Syncfusion ASP.NET MVC Grid allows you to display a loading indicator while loading local data. This feature is useful when there is a delay in loading data from a local source, and you want to inform the you that the data is being fetched.

To display the loading indicator with local data, you need to set the ShowSpinner property to true. This property controls the visibility of the loading indicator.

The following example demonstrates how to display the loading indicator in the Grid using the Load and Created events:

@using Newtonsoft.Json;

@Html.EJS().Grid("grid").Height("348px").Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("150").Add();
    col.Field("Freight").HeaderText("Freight").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Format("C2").Add();
    col.Field("ShipCity").HeaderText("Ship City").Width("150").Add();
}).Created("created").Load("load").AllowPaging().Render()
<script>
    var isDataLoading =true
    var data = JSON.parse('@Html.Raw(JsonConvert.SerializeObject(ViewBag.DataSource))');
    function load() {
        var grid = document.getElementById("grid").ej2_instances[0];
        if (isDataLoading) { 
          grid.showSpinner();
        }
    }
    
    function created(){
        var grid = document.getElementById("grid").ej2_instances[0];
        isDataLoading = true;
        grid.dataSource = data;
    }
</script>
public IActionResult Index()
{
    ViewBag.DataSource = OrderDetails.GetAllRecords();
    return View();
}

Binding data and performing CRUD actions via AJAX request

The Syncfusion ASP.NET MVC Grid provides a seamless way to bind data from external sources using AJAX requests, facilitating CRUD (Create, Read, Update, Delete) operations with data retrieved from a server. This feature is particularly valuable for sending data to a server for database updates and asynchronously retrieving data without refreshing the entire web page

To achieve data binding and perform CRUD actions using AJAX requests in the Grid, follow these steps:

Step 1: To create a new ASP.NET Web Application(.NET Framework) project named AJAX Request, follow these steps:

  • Open Visual Studio.
  • Select “Create a new project”
  • Choose ASP.NET Web Application(.NET Framework) project template.
  • Name the project AJAXRequest.
  • Click “Create”

Step 2: Create a simple Grid by following the Getting Started documentation link.

Step 3: In the Views/Home/Index.cshtml file, follow the steps below.

A. To bind data from an external AJAX request, utilize the DataSource property of the Grid. AJAX data from the server and provide it to the DataSource property using the onSuccess event of the AJAX request.

B. To perform CRUD actions, leverage the ActionBegin event. You can cancel the default CRUD operations by utilizing the cancel argument provided by this event. This allows you to dynamically call your server-side method using AJAX, along with the relevant data received from the ActionBegin event, to update your server data accordingly.

C. In the AJAX success event, you have the flexibility to utilize the Grid endEdit and deleteRecord methods to handle the addition, editing, and deletion of corresponding data in the Grid. However, invoking these methods triggers the ActionBegin event once again to save the changes in the Grid. To prevent this behavior and maintain control over the execution flow, you can employ a flag variable and manage it within the ActionComplete and AJAX failure events: The following code snippet demonstrates this approach:

<div style="padding: 10px 0px 20px 0px">
    @Html.EJS().Button("sample").CssClass("e-success").Content("Bind data via ajax").Render()
</div>
@Html.EJS().Grid("grid").Height("348px").Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").IsPrimaryKey(true).TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Add();
    col.Field("CustomerID").HeaderText("Customer ID").Width("150").Add();
    col.Field("ShipCity").HeaderText("Ship City").Width("150").Add();
    col.Field("ShipCountry").HeaderText("Ship Country").Width("150").Add();
}).ActionBegin("actionBegin").ActionComplete("actionComplete").Toolbar(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" }).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true).Mode(Syncfusion.EJ2.Grids.EditMode.Normal); }).AllowPaging().Render()
<script>
   let flag = false;
   document.getElementById('sample').onclick = function () {
       var grid = document.getElementById("grid").ej2_instances[0];
       const ajax = new ej.base.Ajax("/Home/Getdata", 'POST');
       ajax.send();
        ajax.onSuccess = (data) => {
            grid.dataSource = JSON.parse(data);
        };
    };

    function actionComplete(e) {
        if (e.requestType === 'save' || e.requestType === 'delete') {
            flag = false;
        }
    }

    function actionBegin(e) {
        var grid = document.getElementById("grid").ej2_instances[0];
        if (!flag) {
            if (e.requestType == 'save' && (e.action == 'add')) {
                var editedData = e.data;
                e.cancel = true;
                var ajax = new ej.base.Ajax({
                    url: '/Home/Insert',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify({ value: editedData })
                });
                ajax.onSuccess = () => {
                    flag = true;
                    grid.endEdit();
                };
                ajax.onFailure = () => {
                    flag = false;
                };
                ajax.send();
            }
            if (e.requestType == 'save' && (e.action == "edit")) {
                var editedData = e.data;
                e.cancel = true;
                var ajax = new ej.base.Ajax({
                    url: '/Home/Update',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify({ value: editedData })
                });
                ajax.onSuccess = () => {
                    flag = true;
                    grid.endEdit();
                };
                ajax.onFailure = () => {
                    flag = false;
                };
                ajax.send();
            }
            if (e.requestType == 'delete') {
                var editedData = e.data;
                e.cancel = true;
                var ajax = new ej.base.Ajax({
                    url: '/Home/Delete',
                    type: 'POST',
                    contentType: 'application/json; charset=utf-8',
                    data: JSON.stringify({ key: editedData[0][grid.getPrimaryKeyFieldNames()[0]] })
                });
                ajax.onSuccess = () => {
                    flag = true;
                    grid.deleteRecord();
                };
                ajax.onFailure = () => {
                    flag = false;
                };
                ajax.send();
            }
        }
    }
</script>

Step 7: In the HomeController.cs file under the Controllers folder, there is a method named GetData within the HomeController that provides the data source for the Grid. When the button is clicked, a AJAX request is sent to retrieve the data from the server and bind it to the Grid. Additionally, implement server-side logic to perform add, edit, and delete operations. Add the following code:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using AJAXRequest.Models;
namespace AJAXRequest.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult GetData()
        {
            IEnumerable DataSource = OrdersDetails.GetAllRecords();
            return Json(DataSource);
        }

        public ActionResult Update(OrdersDetails value)
        {
            var ord = value;
            OrdersDetails val = OrdersDetails.GetAllRecords().Where(or => or.OrderID == ord.OrderID).FirstOrDefault();
            val.OrderID = ord.OrderID;
            val.EmployeeID = ord.EmployeeID;
            val.CustomerID = ord.CustomerID;
            val.Freight = ord.Freight;
            val.OrderDate = ord.OrderDate;
            val.ShipCity = ord.ShipCity;
            val.ShipCountry = ord.ShipCountry;
            return Json(value);
        }

        //Insert the record.
        public ActionResult Insert(OrdersDetails value)
        {
            OrdersDetails.GetAllRecords().Insert(0, value);
            return Json(value);
        }

        //Delete the record.
        public ActionResult Delete(int key)
        {
            OrdersDetails.GetAllRecords().Remove(OrdersDetails.GetAllRecords().Where(or => or.OrderID == key).FirstOrDefault());
            var data = OrdersDetails.GetAllRecords();
            return Json(data);
        }
    }
}

Step 5: Create a model class named OrdersDetails.cs under the Models folder in the server-side project to represent the order data. Add the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace AJAXRequest.Models
{
    public class OrdersDetails
    {
        public OrdersDetails() { }
        public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress)
        {
            this.OrderID = OrderID;
            this.CustomerID = CustomerId;
            this.EmployeeID = EmployeeId;
            this.Freight = Freight;
            this.ShipCity = ShipCity;
            this.Verified = Verified;
            this.OrderDate = OrderDate;
            this.ShipName = ShipName;
            this.ShipCountry = ShipCountry;
            this.ShippedDate = ShippedDate;
            this.ShipAddress = ShipAddress;
        }
        
        public static List<OrdersDetails> GetAllRecords()
        {
            List<OrdersDetails> order = new List<OrdersDetails>();
            int code = 10000;
            for (int i = 1; i < 10; i++)
            {
                order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6"));
                order.Add(new OrdersDetails(code + 2, "ANATR", i + 2, 3.3 * i, true, new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", new DateTime(1996, 9, 11), "Avda. Azteca 123"));
                order.Add(new OrdersDetails(code + 3, "ANTON", i + 1, 4.3 * i, true, new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo"));
                order.Add(new OrdersDetails(code + 4, "BLONP", i + 3, 5.3 * i, false, new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", new DateTime(1996, 12, 30), "Magazinweg 7"));
                order.Add(new OrdersDetails(code + 5, "BOLID", i + 4, 6.3 * i, true, new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", new DateTime(1997, 12, 3), "1029 - 12th Ave. S."));
                code += 5;
            }
            return order;
        }
        public int? OrderID { get; set; }
        public string CustomerID { get; set; }
        public int? EmployeeID { get; set; }
        public double? Freight { get; set; }
        public string ShipCity { get; set; }
        public bool Verified { get; set; }
        public DateTime OrderDate { get; set; }
        public string ShipName { get; set; }
        public string ShipCountry { get; set; }
        public DateTime? ShippedDate { get; set; }
        public string ShipAddress { get; set; }
    }
}

The following screenshot represents loading data when the button is clicked and CRUD operations are performed:

Binding data and performing CRUD actions via AJAX request

Display the loading indicator using AJAX

The Syncfusion ASP.NET MVC Grid allows you to display a loading indicator while loading data using AJAX. This feature is useful when there is a delay in loading data from data , and you want to inform the you that the data is being fetched. This is particularly beneficial when working with large datasets or under conditions of slower internet connections.

To display the loading indicator with local data, you need to set the ShowSpinner property to true. This property controls the visibility of the loading indicator.

The following example demonstrates how to display the loading indicator in the Grid using the Load and Created events:

@Html.EJS().Grid("grid").Height("348px").Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("150").Add();
    col.Field("Freight").HeaderText("Freight").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Format("C2").Add();
    col.Field("ShipCity").HeaderText("Ship City").Width("150").Add();
}).Created("created").Load("load").AllowPaging().Render()
<script>
    var isDataLoading =true
    function load() {
        var grid = document.getElementById("grid").ej2_instances[0];
        if (isDataLoading) {
            grid.showSpinner();
            isDataLoading = false;
        }
    }

    function created() {
        var grid = document.getElementById("grid").ej2_instances[0];
        isDataLoading = true;
        const ajax = new ej.base.Ajax('https://services.syncfusion.com/aspnet/production/api/orders', 'GET');
        ajax.send();
        ajax.onSuccess = (data) => {
            grid.dataSource = JSON.parse(data);
        };
    }
</script>
public ActionResult Index()
{
    return View();
}

Managing spinner visibility during data loading

Showing a spinner during data loading in the Syncfusion ASP.NET MVC Grid enhances the experience by providing a visual indication of the loading progress. This feature helps to understand that data is being fetched or processed.

To show or hide a spinner during data loading in the Grid, you can utilize the showSpinner and hideSpinner methods provided by the Grid.

The following example demonstrates how to show and hide the spinner during data loading using external buttons in a Grid:

@using Newtonsoft.Json
<div style="padding-bottom: 10px">
   @Html.EJS().Button("loadButton").CssClass("e-primary").Content("Load Data").Render()
   @Html.EJS().Button("showButton").CssClass("e-primary custom").Content("Show Spinner").Render()
   @Html.EJS().Button("hideButton").CssClass("e-primary custom").Content("Hide Spinner").Render()
</div>
@Html.EJS().Grid("grid").Height("348px").Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("150").Add();
    col.Field("Freight").HeaderText("Freight").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Format("C2").Add();
    col.Field("ShipCountry").HeaderText("Ship Country").Width("150").Add();
}).AllowPaging().Render()
<script>
    document.getElementById('loadButton').onclick = handleButtonClick;
    document.getElementById('showButton').onclick = handleButtonClick;
    document.getElementById('hideButton').onclick = handleButtonClick;
    function handleButtonClick(event){
        var grid = document.getElementById("grid").ej2_instances[0];
        if (event.target.id === "showButton") {
            grid.showSpinner();
        }
        else if(event.target.id === "loadButton"){
            grid.showSpinner();
            setTimeout(() => {
                grid.dataSource = @Html.Raw(JsonConvert.SerializeObject(ViewBag.DataSource));
                grid.hideSpinner();
            }, 1000);
        }
        else {
            grid.hideSpinner();
        }
    }
</script>
<style>
    .custom{
        margin-left: 10px;
    }
</style>
public ActionResult Index()
{
    ViewBag.DataSource = OrderDetails.GetAllRecords();
    return View();
}

Managing spinner visibility during data loading

Immutable mode 

Immutable mode in the Syncfusion ASP.NET MVC Grid is designed to optimize re-rendering performance by utilizing the object reference and deep compare concept. This mode ensures that when performing Grid actions, only the modified or newly added rows are re-rendered, preventing unnecessary re-rendering of unchanged rows.

To enable this feature, you need to set the EnableImmutableMode property as true.

If immutable mode is enabled, when the datasource changes, only newly added rows are regenerated or reused. Consequently, the Grid QueryCellInfo and RowDataBound events trigger only for newly generated rows, not for existing rows.

If immutable mode is not enabled, both newly added rows and existing rows are regenerated or reused when the datasource changes. As a result, the RowDataBound and QueryCellInfo events trigger for both newly added and existing rows.

This feature uses the primary key value for data comparison. So, you need to provide the IsPrimaryKey column.

The following example demonstrates how to enable immutable mode in an ASP.NET MVC component. When add, delete, or update actions are performed, existing rows are not regenerated or reused, ensuring efficient rendering of only the modified or newly added rows:

@using Newtonsoft.Json;
<div style="padding-bottom: 10px">
   @Html.EJS().Button("addtop").CssClass("e-primary").Content("Add rows Data").Render()
   @Html.EJS().Button("delete").CssClass("e-primary custom").Content("Delete rows").Render()
   @Html.EJS().Button("update").CssClass("e-primary custom").Content("Update Freight Data").Render()
</div>
<p style="color:red;text-align:center;padding-bottom:10px" id="message"></p>
@Html.EJS().Grid("grid").DataSource((IEnumerable<object>)ViewBag.dataSource).Height("348px").Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).IsPrimaryKey("true").Width("120").Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("150").Add();
    col.Field("Freight").HeaderText("Freight").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Width("120").Format("C2").Add();
    col.Field("ShipCity").HeaderText("Ship City").Width("150").Add();
    col.Field("ShipName").HeaderText("Ship Name").Width("150").Add();
}).EnableHover().EnableImmutableMode().AllowPaging().QueryCellInfo("queryCellInfo").RowDataBound("rowDataBound").Load("load").SelectionSettings(select => select.Type(Syncfusion.EJ2.Grids.SelectionType.Multiple)).PageSettings(page => { page.PageSize(10); }).Render();
<script>
    var data = JSON.parse('@Html.Raw(JsonConvert.SerializeObject(ViewBag.DataSource))');
    document.getElementById('addtop').onclick = handleButtonClick;
    document.getElementById('delete').onclick = handleButtonClick;
    document.getElementById('update').onclick = handleButtonClick;
    function load(){
        document.getElementById("message").innerText = `Initial rows rendered: ${data.length}`;
    }
    
    function queryCellInfo(args) {
        if ( args.column.field === 'ShipName' && args.data.ShipName === 'Gems Chevalier') {
            args.cell.style.backgroundColor = 'rgb(210, 226, 129)';
        }
    }

    function rowDataBound (args) {
        args.row.style.backgroundColor = args.data.isNewlyAdded ? '' : 'rgb(208, 255, 255)';
    }

    function handleButtonClick(event){
        var grid = document.getElementById("grid").ej2_instances[0];
        if (event.target.id === "addtop") {
            grid.getAllDataRows().forEach(row => {
                row.style.backgroundColor = 'rgb(208, 255, 255)';
            });
            var count = 0;
            if (count < 1) {
                var newRowData = [];
                var addedRecords = {
                    OrderID: generateOrderId(),
                    CustomerID: generateCustomerId(),
                    ShipCity: generateShipCity(),
                    Freight: generateFreight(),
                    ShipName: generateShipName(),
                    isNewlyAdded: true,
                };
                newRowData.push(addedRecords);
                grid.dataSource = [...newRowData, ...grid.dataSource];
                count++;
                document.getElementById("message").innerText = count + ' rows rendered after performing the add action';
            }
        }else if(event.target.id === "delete")
        {
            var count = 0;
            if (count < 1 && data.length > 0) {
                grid.dataSource = grid.dataSource.slice(1);
                count++;
                document.getElementById("message").innerText = count + ' rows deleted after performing delete action';
            }
        }else {
            var count = 0;
            var newRowData = grid.dataSource.map(function (row) {
                if (row.ShipName === 'Bueno Foods') {
                    count++;
                    return { ...row, ShipName: 'Gems Chevalier' };
                } else {
                    return row;
                }
            });
            grid.dataSource = newRowData;
            document.getElementById("message").innerText = count + ' rows updated after performing update action';
        }
    }

    function generateOrderId() {
        return Math.floor(10000 + Math.random() * 90000);
    }

    function generateCustomerId() {
        var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        var result = '';
        for (var i = 0; i < 5; i++) {
            result += characters.charAt(Math.floor(Math.random() * characters.length));
        }
        return result;
    }

    function generateShipCity() {
        var cities = ['London', 'Paris', 'New York', 'Tokyo', 'Berlin'];
        return cities[Math.floor(Math.random() * cities.length)];
    }

    function generateFreight() {
        var randomValue = Math.random() * 100;
        return parseFloat(randomValue.toFixed(2));
    }
    
    function generateShipName() {
        var names = ['Que Delícia', 'Bueno Foods', 'Island Trading', 'Laughing Bacchus Winecellars'];
        return names[Math.floor(Math.random() * names.length)];
    }
</script>
<style>
    .custom{
        margin-left: 10px;
    }
</style>
public ActionResult Index()
{
    ViewBag.DataSource = OrderDetails.GetAllRecords();
    return View();
}

Immutable mode

ExpandoObject binding

The Syncfusion ASP.NET MVC Grid is typically bound to a specific model type. However, there are scenarios where the model type is unknown during compile time. In such cases, you can bind data to the Grid using a list of ExpandoObject. This allows for dynamic data structures that can adapt to various data shapes without a predefined schema.

To bind an ExpandoObject to the Grid, you need to assign it to the DataSource property. The Grid supports various data operations such as sorting, filtering, and editing when using ExpandoObject.

The following sample demonstrates ExpandoObject binding:

@Html.EJS().Grid("grid").DataSource((IEnumerable<object>)ViewBag.ExpandoData).AllowSorting().AllowFiltering().Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").IsPrimaryKey(true).Width("140").ValidationRules(new { required = true, number = true }).TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("150").ValidationRules(new { required = true, minLength = 3 }).Add();
    col.Field("Freight").HeaderText("Freight").Width("140").EditType("numericedit").ValidationRules(new { required = true }).Format("C2").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Left).Add();
    col.Field("ShipCountry").HeaderText("Ship Country").Width("150").EditType("dropdownedit").Add();
}).AllowPaging().PageSettings(page => page.PageCount(10)).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true).Mode(Syncfusion.EJ2.Grids.EditMode.Normal); }).Toolbar(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" }).Render()
public static List<ExpandoObject> ExpandoOrders { get; set; } = new List<ExpandoObject>();
public ActionResult Index()
{
    string[] customerIDs = { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" };
    string[] shipCountrys = { "USA", "UK", "Denmark", "Australia", "India" };
    ExpandoOrders = Enumerable.Range(1, 75).Select((x) =>
    {
        dynamic order = new ExpandoObject();
        order.OrderID = 1000 + x;
        order.CustomerID = customerIDs[x % customerIDs.Length];
        order.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
        order.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
        order.ShipCountry = shipCountrys[x % shipCountrys.Length];
        return order;
    }).Cast<ExpandoObject>().ToList<ExpandoObject>();
    ViewBag.ExpandoData = ExpandoOrders;
    return View();
}

ExpandoObject with complex column binding

You can achieve complex data binding with ExpandoObject in the Syncfusion ASP.NET MVC Grid by using the dot (.) operator in the Column.Field property. This feature allows you to access and bind to nested properties within the ExpandoObject, enabling more structured data representation in the Grid.

In the following example, the fields Customer.CustomerID, Customer.OrderDate, Customer.Freight, and Customer.ShipCountry represent complex data that is bound to the Grid:

@Html.EJS().Grid("grid").DataSource((IEnumerable<object>)ViewBag.ExpandoData).AllowSorting().AllowFiltering().Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").IsPrimaryKey(true).Width("140").ValidationRules(new { required = true, number = true }).TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
    col.Field("Customer.CustomerID").HeaderText("Customer ID").Width("150").Add();
    col.Field("Customer.Freight").HeaderText("Freight").Width("140").EditType("numericedit").ValidationRules(new { required = true }).Format("C2").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Left).Add();
    col.Field("Customer.ShipCountry").HeaderText("Ship Country").Width("150").EditType("dropdownedit").Add();
}).AllowPaging().EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true).Mode(Syncfusion.EJ2.Grids.EditMode.Normal); }).Toolbar(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" }).Render()
public static List<ExpandoObject> ExpandoOrders { get; set; } = new List<ExpandoObject>();
public ActionResult Index()
{
    string[] customerID = { "John Doe", "Jane Smith", "Alice Johnson", "Bob Brown", "Charlie Davis" };
    string[] shipCountrys = { "USA", "UK", "Denmark", "Australia", "India" };

    ExpandoOrders = Enumerable.Range(1, 75).Select((x) =>
    {
        dynamic order = new ExpandoObject();
       order.OrderID = 1000 + x;
       order.Customer = new ExpandoObject();
       order.Customer.CustomerID = customerID[x % customerID.Length];
       order.Customer.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
       order.Customer.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
       order.Customer.ShipCountry = shipCountrys[x % shipCountrys.Length];;
        return order;
    }).Cast<ExpandoObject>().ToList<ExpandoObject>();
    ViewBag.ExpandoData = ExpandoOrders;
    return View();
}

NOTE

Perform data and CRUD operations for complex ExpandoObject binding fields as well.

DynamicObject binding

The Syncfusion ASP.NET MVC Grid component is typically bound to a specific model type. However, there are scenarios where the model type is unknown during compile time. In such cases, you can bind data to the Grid using a list of ExpandoObject. This allows for dynamic data structures that can adapt to various data shapes without a predefined schema.

To bind an ExpandoObject to the Grid, you need to assign it to the DataSource property. This enables the Grid to perform various supported data operations and editing on the DynamicObject.

NOTE

You must override the GetDynamicMemberNames method of the DynamicObject class and return the property names to perform data operation and editing while using DynamicObject.

Here’s an example of how to bind a list of DynamicObject to the Grid:

@Html.EJS().Grid("grid").DataSource((IEnumerable<object>)ViewBag.DynamicData).AllowSorting().AllowFiltering().Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").IsPrimaryKey(true).Width("140").ValidationRules(new { required = true, number = true }).TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
    col.Field("CustomerID").HeaderText("Customer Name").Width("150").ValidationRules(new { required = true, minLength = 3 }).Add();
    col.Field("Freight").HeaderText("Freight").Width("140").EditType("numericedit").ValidationRules(new { required = true }).Format("C2").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
    col.Field("ShipCountry").HeaderText("Ship Country").Width("150").EditType("dropdownedit").Add();
}).AllowPaging().PageSettings(page => page.PageCount(10)).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true).Mode(Syncfusion.EJ2.Grids.EditMode.Normal); }).Toolbar(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" }).Render()
public static List<DynamicList> DynamicOrders { get; set; } = new List<DynamicList>();
public ActionResult Index()
{
    string[] customerIDList = { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" };
    string[] shipCountrys = { "USA", "UK", "Denmark", "Australia", "India" };
    DynamicOrders = Enumerable.Range(1, 75).Select((x) =>
    {
        dynamic order = new DynamicList();
       order.OrderID = 1000 + x;
       order.CustomerID = customerIDList[x % customerIDList.Length];
       order.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
       order.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
       order.ShipCountry = shipCountrys[x % shipCountrys.Length];
        return order;
    }).Cast<DynamicList>().ToList<DynamicList>();
    ViewBag.DynamicData = DynamicOrders;
    return View();
}

public class DynamicList : DynamicObject
{
    private List<KeyValuePair<string, object>> properties = new List<KeyValuePair<string, object>>();
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        string name = binder.Name;
        var property = properties.Find(propertyItem => propertyItem.Key == name);
        result = property.Value;
        return property.Key != null;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        string name = binder.Name;
        var property = properties.Find(propertyItem => propertyItem.Key == name);
        if (property.Key != null)
        {
            properties.Remove(property);
        }
        properties.Add(new KeyValuePair<string, object>(name, value));
        return true;
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return properties.ConvertAll(propertyItem => propertyItem.Key);
    }
}

DynamicObject with complex column binding

You can achieve complex data binding with DynamicObject in the Syncfusion ASP.NET MVC Grid by using the dot (.) operator in the Column.Field property. This allows you to access and bind to nested properties within the DynamicObject, enabling the display of structured data in the Grid.

In the following example, Customer.OrderDate, Customer.Freight, and Customer.ShipCountry are considered complex data fields that are bound to the Grid:

@Html.EJS().Grid("grid").DataSource((IEnumerable<object>)ViewBag.DynamicData).AllowSorting().AllowFiltering().Columns(col =>
{
    col.Field("OrderID").HeaderText("Order ID").IsPrimaryKey(true).Width("140").ValidationRules(new { required = true, number = true }).TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
    col.Field("Customer.CustomerID").HeaderText("Customer ID").Width("150").Add();
    col.Field("Customer.Freight").HeaderText("Freight").Width("140").EditType("numericedit").ValidationRules(new { required = true }).Format("C2").TextAlign(Syncfusion.EJ2.Grids.TextAlign.Right).Add();
    col.Field("Customer.OrderDate").HeaderText("Order Date").EditType("datetimepickeredit").Width("150").Format(new { type = "datetime", format = "M/d/y hh:mm a" }).Add();
    col.Field("Customer.ShipCountry").HeaderText("Ship Country").Width("150").EditType("dropdownedit").Add();
}).AllowPaging().PageSettings(page => page.PageCount(10)).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true).Mode(Syncfusion.EJ2.Grids.EditMode.Normal); }).Toolbar(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" }).Render()
public static List<DynamicList> DynamicOrders { get; set; } = new List<DynamicList>();
public ActionResult Index()
{
    string[] customerID = { "John Doe", "Jane Smith", "Alice Johnson", "Bob Brown", "Charlie Davis" };
    string[] shipCountrys = { "USA", "UK", "Denmark", "Australia", "India" };
    DynamicOrders = Enumerable.Range(1, 75).Select((x) =>
    {
        dynamic order = new DynamicList();
       order.OrderID = 1000 + x;
       order.Customer = new DynamicList();
       order.Customer.CustomerID = customerID[x % customerID.Length];
       order.Customer.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
       order.Customer.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
       order.Customer.ShipCountry = shipCountrys[x % shipCountrys.Length];
        return order;
    }).Cast<DynamicList>().ToList<DynamicList>();
    ViewBag.DynamicData = DynamicOrders;
    return View();
}

public class DynamicList : DynamicObject
{
    private List<KeyValuePair<string, object>> properties = new List<KeyValuePair<string, object>>();
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        string name = binder.Name;
        var property = properties.Find(propertyItem => propertyItem.Key == name);
        result = property.Value;
        return property.Key != null;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        string name = binder.Name;
        var property = properties.Find(propertyItem => propertyItem.Key == name);
        if (property.Key != null)
        {
            properties.Remove(property);
        }
        properties.Add(new KeyValuePair<string, object>(name, value));
        return true;
    }
    
    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return properties.ConvertAll(propertyItem => propertyItem.Key);
    }
}

NOTE

Perform data and CRUD operations for complex DynamicObject binding fields as well.

Limitations

The following features are not supported in the immutable mode:

  • Frozen rows and columns
  • Grouping
  • Row Template
  • Detail Template
  • Hierarchy Grid
  • Scrolling
  • Virtual scroll
  • Infinite scroll
  • Column reorder
  • Rows,column spanning
  • PDF export ,Excel export,Print
  • Column Resize
  • Drag and drop
  • Column template
  • Column chooser
  • Clipboard
  • AutoFit
  • Filtering