How can I help you?
Local data in Vue Grid component
30 Mar 202624 minutes to read
The Syncfusion® Vue Grid provides a straightforward way to bind local data, such as arrays or JSON objects, to the grid component. This feature allows data to be displayed and manipulated within the grid without the need for external server calls, making it particularly useful for scenarios involving static or locally stored data.
Assign a JavaScript object array to the dataSource property. Optionally, provide the local data source using an instance of the DataManager.
The following example demonstrates the local data binding feature in the Vue Grid component:
<template>
<div id="app">
<ejs-grid :dataSource="data" height='315px' >
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
<e-column field='CustomerID' headerText='Customer ID' width=120></e-column>
<e-column field='Freight' headerText='Freight' textAlign='Right' format='C2' width=90></e-column>
<e-column field='OrderDate' headerText='Order Date' textAlign='Right' format='yMd' type='date' width=120></e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script setup>
import { GridComponent as EjsGrid, ColumnDirective as EColumn, ColumnsDirective as EColumns } from "@syncfusion/ej2-vue-grids";
import { data } from './datasource.js';
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style><template>
<div id="app">
<ejs-grid :dataSource="data" height='315px' >
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
<e-column field='CustomerID' headerText='Customer ID' width=120></e-column>
<e-column field='Freight' headerText='Freight' textAlign='Right' format='C2' width=90></e-column>
<e-column field='OrderDate' headerText='Order Date' textAlign='Right' format='yMd' type='date' width=120></e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective } from "@syncfusion/ej2-vue-grids";
import { data } from './datasource.js';
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective
},
data() {
return {
data: data
};
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>Data binding with SignalR
The Syncfusion® Grid provides support for real-time data binding using SignalR, enabling automatic Grid updates when data changes on the server side. This feature is particularly useful for applications that require live updates and synchronization across multiple clients.
To achieve real-time data binding with SignalR in the Syncfusion® Vue Grid, follow the steps outlined below.
Step 1: Open Visual Studio and create an Vue and ASP.NET Core project named signalR. To create an Vue and ASP.NET Core application, follow the documentation link for detailed steps.
Step 2 : Create a simple Vue Grid by following the Getting Started documentation link.
Step 3: Install the required SignalR package for the client application using npm:
npm install @microsoft/signalr — saveStep 4: Create a SignalR hub on the server side to manage communication between clients and the server. A ChatHub.cs file can be created under the Hubs folder. Add the following code to define methods for sending data updates to clients:
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Hubs
{
public class ChatHub : Hub
{
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
}Step 5: Configure the SignalR server to route requests to the SignalR hub. In the Program.cs file, add the following code:
using SignalR.Server.Hubs;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSignalR();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
{
options.AddPolicy("CORSPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.SetIsOriginAllowed((hosts) =>
true));
});
var app = builder.Build();
app.UseCors("CORSPolicy");
app.UseDefaultFiles();
app.UseStaticFiles();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapHub<ChatHub>("/chatHub");
app.MapFallbackToFile("/index.html");
app.Run();Step 6: In the client-side code, establish a connection to the SignalR hub and configure Grid data binding in the App.vue file.
<template>
<div id="app">
<ejs-grid ref="grid" :dataSource='data' :toolbar="toolbar" :editSettings="editSettings" allowSorting="true" allowPaging="true" :created="created" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' width='120' textAlign='Right' isPrimaryKey="true"></e-column>
<e-column field='CustomerID' headerText='Customer ID' width='160'></e-column>
<e-column field='ShipCity' headerText='Ship City' width='150'></e-column>
<e-column field='ShipCountry' headerText='Ship Country' width='150'></e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script>
import { provide } from "vue";
import { GridComponent, ColumnDirective , ColumnsDirective, Page, Edit, Sort, Toolbar } from "@syncfusion/ej2-vue-grids";
import { DataManager, UrlAdaptor } from "@syncfusion/ej2-data";
import * as signalR from '@microsoft/signalr';
export default {
name: "App",
components: {
"ejs-grid": GridComponent,
"e-columns": ColumnsDirective,
"e-column": ColumnDirective,
},
data() {
return {
data: new DataManager({
url: 'https://localhost:****/api/Home',
insertUrl: 'https://localhost:****/api/Home/Insert',
updateUrl: 'https://localhost:****/api/Home/Update',
removeUrl: 'https://localhost:****/api/Home/Remove',
adaptor: new UrlAdaptor(),
}),//Use remote server host number instead ****
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal' },
toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel'],
connection: new signalR.HubConnectionBuilder().withUrl("https://localhost:****/ChatHub").build() //Use remote server host number instead ****
};
},
methods: {
created: function (e) {
this.connection.on("ReceiveMessage", (message) => {
if (this.$refs.grid) {
this.$refs.grid.ej2Instances.refresh();
}
});
this.connection.start()
.then(() => {
console.log("SignalR connection established successfully");
this.connection.invoke('SendMessage', "refreshPages")
.catch((err) => {
console.error("Error sending data:", err.toString());
});
})
.catch((err) => {
console.error("Error establishing SignalR connection:", err.toString());
});
},
actionComplete: function (args) {
if (args.requestType === "save" || args.requestType === "delete") {
this.connection.invoke('SendMessage', "refreshPages")
.catch((err) => {
console.error(err.toString());
});
}
}
},
provide: {
grid: [Sort, Edit, Page, Toolbar]
},
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style><template>
<div id="app">
<ejs-grid ref="grid" :dataSource='data' :toolbar="toolbar" :editSettings="editSettings" allowSorting="true" allowPaging="true" :created="created" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' width='120' textAlign='Right' isPrimaryKey="true"></e-column>
<e-column field='CustomerID' headerText='Customer ID' width='160'></e-column>
<e-column field='Freight' headerText='Freight' format="C" width='150'></e-column>
<e-column field='ShipCity' headerText='Ship City' width='150'></e-column>
<e-column field='ShipCountry' headerText='Ship Country' width='150'></e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script setup>
import { provide, ref } from "vue";
import { GridComponent as EjsGrid, ColumnDirective as EColumn, ColumnsDirective as EColumns, Page, Edit, Sort, Filter, Toolbar } from "@syncfusion/ej2-vue-grids";
import { DataManager, UrlAdaptor } from "@syncfusion/ej2-data";
import * as signalR from '@microsoft/signalr';
const grid = ref(null);
const data = new DataManager({
url: 'https://localhost:****/api/Home',
insertUrl: 'https://localhost:****/api/Home/Insert',
updateUrl: 'https://localhost:****/api/Home/Update',
removeUrl: 'https://localhost:****/api/Home/Remove',
adaptor: new UrlAdaptor(),
});//Use remote server host number instead ****
let connection = new signalR.HubConnectionBuilder().withUrl("https://localhost:****/ChatHub").build();//Use remote server host number instead ****
const editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true };
const toolbar = ['Add', 'Edit', 'Update', 'Delete', 'Search'];
const created = function () {
connection.on("ReceiveMessage", (message) => {
if (grid.value) {
grid.value.ej2Instances.refresh();
}
});
connection.start()
.then(() => {
console.log("SignalR connection established successfully");
connection.invoke('SendMessage', "refreshPages")
.catch((err) => {
console.error("Error sending data:", err.toString());
});
})
.catch((err) => {
console.error("Error establishing SignalR connection:", err.toString());
});
};
const actionComplete = function (args) {
if (args.requestType === "save" || args.requestType === "delete") {
connection.invoke('SendMessage', "refreshPages")
.catch((err) => {
console.error(err.toString());
});
}
}
provide('grid', [Sort, Edit, Filter, Page, Toolbar]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>Step 6 : Create a controller on the server side to manage data operations such as fetching, updating, inserting, and deleting records. A HomeController.cs file can be created under the Controllers folder. Add the following code to define methods for sending data updates to clients.
using Microsoft.AspNetCore.Mvc;
using SignalR.Server.Models;
using Microsoft.AspNetCore.Http;
using Syncfusion.EJ2.Base;
namespace SignalR.Server.Controllers
{
[ApiController]
public class HomeController : Controller
{
[HttpPost]
[Route("api/[controller]")]
public object Post([FromBody] DataManagerRequest DataManagerRequest)
{
// Retrieve data from the data source (e.g., database)
IQueryable<OrdersDetails> DataSource = GetOrderData().AsQueryable();
QueryableOperation queryableOperation = new QueryableOperation(); // Initialize DataOperations instance
// Handling searching operation
if (DataManagerRequest.Search != null && DataManagerRequest.Search.Count > 0)
{
DataSource = queryableOperation.PerformSearching(DataSource, DataManagerRequest.Search);
}
// Handling filtering operation
if (DataManagerRequest.Where != null && DataManagerRequest.Where.Count > 0)
{
foreach (var condition in DataManagerRequest.Where)
{
foreach (var predicate in condition.predicates)
{
DataSource = queryableOperation.PerformFiltering(DataSource, DataManagerRequest.Where, predicate.Operator);
}
}
}
// Handling sorting operation
if (DataManagerRequest.Sorted != null && DataManagerRequest.Sorted.Count > 0)
{
DataSource = queryableOperation.PerformSorting(DataSource, DataManagerRequest.Sorted);
}
// Get the total count of records
int totalRecordsCount = DataSource.Count();
// Handling paging operation.
if (DataManagerRequest.Skip != 0)
{
DataSource = queryableOperation.PerformSkip(DataSource, DataManagerRequest.Skip);
}
if (DataManagerRequest.Take != 0)
{
DataSource = queryableOperation.PerformTake(DataSource, DataManagerRequest.Take);
}
// Return data based on the request
return new { result = DataSource, count = totalRecordsCount };
}
[HttpGet]
[Route("api/[controller]")]
public List<OrdersDetails> GetOrderData()
{
var data = OrdersDetails.GetAllRecords().ToList();
return data;
}
/// <summary>
/// Inserts a new data item into the data collection.
/// </summary>
/// <param name="newRecord">It contains the new record detail which is need to be inserted.</param>
/// <returns>Returns void</returns>
[HttpPost]
[Route("api/Home/Insert")]
public void Insert([FromBody] CRUDModel<OrdersDetails> newRecord)
{
if (newRecord.value != null)
{
OrdersDetails.GetAllRecords().Insert(0, newRecord.value);
}
}
/// <summary>
/// Update a existing data item from the data collection.
/// </summary>
/// <param name="Order">It contains the updated record detail which is need to be updated.</param>
/// <returns>Returns void</returns>
[HttpPost]
[Route("api/Home/Update")]
public void Update([FromBody] CRUDModel<OrdersDetails> Order)
{
var updatedOrder = Order.value;
if (updatedOrder != null)
{
var data = OrdersDetails.GetAllRecords().FirstOrDefault(or => or.OrderID == updatedOrder.OrderID);
if (data != null)
{
// Update the existing record
data.OrderID = updatedOrder.OrderID;
data.CustomerID = updatedOrder.CustomerID;
data.ShipCity = updatedOrder.ShipCity;
data.ShipCountry = updatedOrder.ShipCountry;
}
}
}
/// <summary>
/// Remove a specific data item from the data collection.
/// </summary>
/// <param name="value">It contains the specific record detail which is need to be removed.</param>
/// <return>Returns void</return>
[HttpPost]
[Route("api/Home/Remove")]
public void Remove([FromBody] CRUDModel<OrdersDetails> value)
{
int orderId = int.Parse((value.key).ToString());
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 7 : Define a model class to represent the data structure. An OrdersDetails.cs file can be created under the Models folder. Add the following code.
namespace SignalR.Server.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 < 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 the addition, editing, and deletion operations performed, reflecting changes across all client sides.

A complete SignalR sample is available on GitHub.
Binding data from excel file
The Syncfusion® Vue Grid supports importing data from Excel files for display and manipulation within the grid. This streamlines transferring Excel data into a web-based environment. Use the Uploader component’s change event to handle the import.
To import Excel data into the Grid, follow these steps:
- Import excel file using
Uploadercomponent. - Parse the excel file data using
XLSXlibrary. - Bind the resulting JSON to the grid.
The following example demonstrates importing Excel data into the grid by utilizing the Uploader component’s change event along with the XLSX library:
<template>
<div id="app">
<div>
<label style="font-weight: bold">Browse excel file to load and return grid</label>
<div style="padding:20px 0 0 0">
<ejs-uploader :asyncSettings='path' :removing="onRemove" :change="onSuccess"></ejs-uploader>
</div>
</div>
<ejs-grid ref="grid" >
</ejs-grid>
<ejs-dialog ref="dialog" width="350" :visible='false' header="Alert" content='Invalid JSON' showCloseIcon='true' >
</ejs-dialog>
</div>
</template>
<script setup>
import { provide,ref } from "vue";
import { GridComponent as EjsGrid, Page } from "@syncfusion/ej2-vue-grids";
import { UploaderComponent as EjsUploader } from "@syncfusion/ej2-vue-inputs";
import { DialogComponent as EjsDialog } from "@syncfusion/ej2-vue-popups";
import * as XLSX from 'xlsx';
const grid=ref(null);
const dialog=ref(null);
const path ={
saveUrl: 'https://services.syncfusion.com/vue/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/vue/production/api/FileUploader/Remove'
};
const parseExcel=function(file) {
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.value.ej2Instances.dataSource = XL_row_object;
} else {
dialog.value.ej2Instances.content = "Invalid JSON";
dialog.value.show();
}
});
};
reader.readAsArrayBuffer(file.rawFile);
} else {
dialog.value.ej2Instances.content = "Please upload only .xlsx format";
dialog.value.show();
}
};
const onSuccess=function(args){
var files = args.file;
if (files) {
parseExcel(files[0]);
}
};
const onRemove=function() {
grid.value.ej2Instances.dataSource = [""];
grid.value.ej2Instances.columns = [];
}
provide('grid', [Page]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style><template>
<div id="app">
<div>
<label style="font-weight: bold">Browse excel file to load and return grid</label>
<div style="padding:20px 0 0 0">
<ejs-uploader :asyncSettings='path' :removing="onRemove" :change="onSuccess"></ejs-uploader>
</div>
</div>
<ejs-grid ref="grid" >
</ejs-grid>
<ejs-dialog ref="dialog" width="350" :visible='false' header="Alert" content='Invalid JSON' showCloseIcon='true' >
</ejs-dialog>
</div>
</template>
<script>
import { GridComponent,Page} from "@syncfusion/ej2-vue-grids";
import { DialogComponent } from "@syncfusion/ej2-vue-popups";
import { UploaderComponent } from "@syncfusion/ej2-vue-inputs";
import * as XLSX from 'xlsx';
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"ejs-dialog":DialogComponent,
"ejs-uploader":UploaderComponent
},
data() {
return {
path :{
saveUrl: 'https://services.syncfusion.com/vue/production/api/FileUploader/Save',
removeUrl: 'https://services.syncfusion.com/vue/production/api/FileUploader/Remove'
}
};
},
methods: {
parseExcel(file) {
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) {
this.$refs.grid.ej2Instances.dataSource = XL_row_object;
} else {
this.$refs.dialog.ej2Instances.content = "Invalid JSON";
this.$refs.dialog.ej2Instances.show();
}
});
};
reader.readAsArrayBuffer(file.rawFile);
} else {
this.$refs.dialog.ej2Instances.content = "Please upload only .xlsx format";
this.$refs.dialog.ej2Instances.show();
}
},
onSuccess(args){
var files = args.file;
if (files) {
this.parseExcel(files[0]);
}
},
onRemove() {
this.$refs.grid.ej2Instances.dataSource = [""];
this.$refs.grid.ej2Instances.columns = [];
}
},
provide: {
grid: [Page]
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>Binding data and performing CRUD actions via Fetch request
The Syncfusion® 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 Ajax requests in the Syncfusion® Grid, follow these steps:
Step 1: Open Visual Studio and create an Vue and ASP.NET Core project named FetchProject. To create an Vue and ASP.NET Core application, follow the documentation link for detailed steps.
Step 2: Include the Syncfusion® Grid in the HTML file with the necessary configurations:
<ejs-button cssClass='e-success' @click="click">Bind data via Fetch</ejs-button>
<div style="padding: 20px 17px 0 0">
<ejs-grid ref="grid" :dataSource='data' :editSettings='editSettings' :toolbar='toolbar' allowPaging="true" height="320" :actionBegin="actionBegin" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' isPrimaryKey=true width='150'></e-column>
<e-column field='CustomerID' headerText='Customer Name' width='150'></e-column>
<e-column field='Freight' headerText='Freight' format="C" width='150'></e-column>
<e-column field='ShipCity' headerText='ShipCity' width='150' textAlign='Right'></e-column>
</e-columns>
</ejs-grid>
</div>Step 3: 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.
click: function () {
const fetchRequest = new fetch("https://localhost:****/Home/Getdata", 'POST');//Use remote server host number instead ****
fetchRequest.send();
fetchRequest.onSuccess = (data) => {
this.$refs.grid.ej2Instances.dataSource = data;
};
}On the server side, there is a method named “GetData” within the HomeController that contains the grid’s data source. When the button is clicked, a Fetch request is sent to fetch the data from the server and bind it to the Grid component.
public class HomeController : Controller
{
public ActionResult Getdata()
{
IEnumerable DataSource = OrdersDetails.GetAllRecords();
return Json(DataSource);
}
}Create a model class named OrdersDetails.cs in the server-side Models folder to represent the order data.
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;
}
//Render data in this method
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; }
}Step 4: To perform CRUD actions, leverage the actionBegin event. The default CRUD operations can be canceled by utilizing the cancel argument provided by this event. This allows dynamic invocation of server-side methods using Fetch, along with the relevant data received from the actionBegin event, to update server data accordingly.
A. To add and save a new record using Fetch requests, follow these steps:
public flag = false;
actionBegin: function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!this.flag) {
// Add and edit operations
if (e.requestType == 'save' && (e.action == 'add')) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using a Fetch call
var fetchRequest= new Fetch({
url: 'https://localhost:****/Home/Insert', //Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
fetchRequest.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
this.flag = true;
//The added/edited data will be saved in the Grid
this.$refs.grid.ej2Instances.endEdit();
};
fetchRequest.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
this.flag = false;
};
fetchRequest.send();
}
}
}//insert the record in server side
public ActionResult Insert(OrdersDetails value)
{
OrdersDetails.GetAllRecords().Insert(0, value);
return Json(value);
}B. To edit and save a record using an Fetch request, follow these steps:
public flag = false;
actionBegin: function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!this.flag) {
if (e.requestType == 'save' && (e.action == "edit")) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using a Fetch call
var fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Update', //Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
fetchRequest.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
this.flag = true;
// The added/edited data will be saved in the Grid
this.$refs.grid.ej2Instances.endEdit();
};
fetchRequest.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next executio
this.flag = false;
};
fetchRequest.send();
}
}
}//update the record in server side
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;
return Json(value);
}C. To delete a record using an Fetch request, follow these steps:
public flag = false;
actionBegin: function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!this.flag) {
if (e.requestType == 'delete') {
var editedData = e.data;
// The default delete operation is cancelled
e.cancel = true;
// The deleted data can be sent to the server using a Fetch call
var fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Delete', //Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ key: editedData[0][this.$refs.grid.ej2Instances.getPrimaryKeyFieldNames()[0]] })
});
fetchRequest.onSuccess = () => {
// this.flag is enabled to skip this execution when grid deletes record
this.flag = true;
// The deleted data will be removed in the Grid
this.$refs.grid.ej2Instances.deleteRecord();
};
fetchRequest.onFailure = () => {
// Delete failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
this.flag = false;
};
fetchRequest.send();
}
}
}//Delete the record in server side
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: In the Fetch success event, the Grid’s endEdit and deleteRecord methods can be utilized 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, a flag variable can be employed and managed within the actionComplete and Fetch failure events. The following code snippet demonstrates this approach:
actionComplete: function (e){
if (e.requestType === 'save' || e.requestType === 'delete') {
// The this.flag is disabled after operation is successfully performed so that it can enter the condition on next execution
this.flag = false;
}
}The following screenshot represents loading data when the button is clicked and CRUD operations are performed:
<template>
<div id="app">
<ejs-button cssClass='e-success' @click="click">Bind data via fetch</ejs-button>
<div style="padding: 20px 17px 0 0">
<ejs-grid ref="grid" :editSettings='editSettings' :toolbar='toolbar' allowPaging="true" height="320" :actionBegin="actionBegin" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' isPrimaryKey=true width='150'></e-column>
<e-column field='CustomerID' headerText='Customer Name' width='150'></e-column>
<e-column field='Freight' headerText='Freight' format="C" width='150'></e-column>
<e-column field='ShipCity' headerText='ShipCity' width='150' textAlign='Right'></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script setup>
import { provide, ref } from "vue";
import { GridComponent as EjsGrid, ColumnDirective as EColumn, ColumnsDirective as EColumns, Page, Edit, Toolbar } from "@syncfusion/ej2-vue-grids";
import { ButtonComponent as EjsButton } from "@syncfusion/ej2-vue-buttons";
import { Fetch } from '@syncfusion/ej2-base';
const grid = ref(null);
let flag = false;
const editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal' };
const toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
const click = function () {
const fetchRequest = new Fetch("https://localhost:****/Home/Getdata", 'POST'); // Use remote server host number instead ****
fetchRequest.send();
fetchRequest.onSuccess = (data) => {
grid.value.ej2Instances.dataSource = data;
};
};
const actionBegin = function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!flag) {
// Add and edit operations
if (e.requestType === 'save' && (e.action === 'add')) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using a Fetch call
const fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Insert',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
}); // Use remote server host number instead ****
fetchRequest.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
flag = true;
// The added/edited data will be saved in the Grid
grid.value.ej2Instances.endEdit();
};
fetchRequest.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
flag = false;
};
fetch.send();
}
if (e.requestType === 'save' && (e.action === "edit")) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using a Fetch call
const fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Update',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
}); // Use remote server host number instead ****
fetchRequest.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
flag = true;
// The added/edited data will be saved in the Grid
grid.value.ej2Instances.endEdit();
};
fetchRequest.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
flag = false;
};
fetchRequest.send();
}
if (e.requestType === 'delete') {
var editedData = e.data;
// The default delete operation is cancelled
e.cancel = true;
// The deleted data can be sent to the server using a Fetch call
const fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Delete', //Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ key: editedData[0][grid.value.ej2Instances.getPrimaryKeyFieldNames()[0]] })
}); // Use remote server host number instead ****
fetchRequest.onSuccess = () => {
// this.flag is enabled to skip this execution when grid deletes record
flag = true;
// The deleted data will be removed in the Grid
grid.value.ej2Instances.deleteRecord();
};
fetchRequest.onFailure = () => {
// Delete failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
flag = false;
};
fetchRequest.send();
}
}
};
const actionComplete = function (e) {
if (e.requestType === 'save' || e.requestType === 'delete') {
// The this.flag is disabled after operation is successfully performed so that it can enter the condition on next execution
flag = false;
}
};
provide('grid', [Page, Edit, Toolbar]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
</style><template>
<div id="app">
<ejs-button cssClass='e-success' @click="click">Bind data via fetch</ejs-button>
<div style="padding: 20px 17px 0 0">
<ejs-grid ref="grid" :editSettings='editSettings' :toolbar='toolbar' allowPaging="true" height="320" :actionBegin="actionBegin" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' isPrimaryKey=true width='150'></e-column>
<e-column field='CustomerID' headerText='Customer Name' width='150'></e-column>
<e-column field='Freight' headerText='Freight' format="C" width='150'></e-column>
<e-column field='ShipCity' headerText='ShipCity' width='150' textAlign='Right'></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective, Page, Edit, Toolbar } from "@syncfusion/ej2-vue-grids";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import { Fetch } from '@syncfusion/ej2-base';
export default {
name: "App",
components: {
"ejs-grid": GridComponent,
"e-columns": ColumnsDirective,
"e-column": ColumnDirective,
"ejs-button": ButtonComponent
},
data() {
return {
flag: false,
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal' },
toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel']
};
},
methods: {
click: function () {
const fetchRequest = new Fetch("https://localhost:****/Home/Getdata", 'POST'); //Use remote server host number instead ****
fetchRequest.send();
fetchRequest.onSuccess = (data) => {
this.$refs.grid.ej2Instances.dataSource = data;
};
},
actionBegin: function (e) {
if (!this.flag) {
if (e.requestType == 'save' && (e.action == 'add')) {
var editedData = e.data;
e.cancel = true;
var fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Insert',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
fetchRequest.onSuccess = () => {
this.flag = true;
this.$refs.grid.ej2Instances.endEdit();
};
fetchRequest.onFailure = () => {
this.flag = false;
};
fetchRequest.send();
}
if (e.requestType == 'save' && (e.action == "edit")) {
var editedData = e.data;
e.cancel = true;
var fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Update',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
fetchRequest.onSuccess = () => {
this.flag = true;
this.$refs.grid.ej2Instances.endEdit();
};
fetchRequest.onFailure = () => {
this.flag = false;
};
fetchRequest.send();
}
if (e.requestType == 'delete') {
var editedData = e.data;
e.cancel = true;
var fetchRequest = new Fetch({
url: 'https://localhost:****/Home/Delete',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ key: editedData[0][this.$refs.grid.ej2Instances.getPrimaryKeyFieldNames()[0]] })
});
fetchRequest.onSuccess = () => {
this.flag = true;
this.$refs.grid.ej2Instances.deleteRecord();
};
fetchRequest.onFailure = () => {
this.flag = false;
};
fetchRequest.send();
}
}
},
actionComplete: function (e) {
if (e.requestType === 'save' || e.requestType === 'delete') {
this.flag = false;
}
}
},
provide: {
grid: [Page, Edit, Toolbar]
},
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>using FetchProject.Server.Models;
using Microsoft.AspNetCore.Mvc;
namespace FetchProject.Server.Controllers
{
public class HomeController : Controller
{
public ActionResult Getdata()
{
var DataSource = OrdersDetails.GetAllRecords();
return Json(DataSource);
}
public ActionResult Update([FromBody] CRUDModel<OrdersDetails> value)
{
var ord = value.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.value);
}
//insert the record
public ActionResult Insert([FromBody] CRUDModel<OrdersDetails> value)
{
OrdersDetails.GetAllRecords().Insert(0, value.value);
return Json(value.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);
}
}
}
public class CRUDModel<T> where T : class
{
public string action { get; set; }
public string table { 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; }
}namespace FetchProject.Server.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()
{
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; }
}
}var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
});
});
var app = builder.Build();
// Configure HTTPS redirection
app.UseHttpsRedirection();
// Configure CORS
app.UseCors();
app.UseDefaultFiles();
app.UseStaticFiles();
// Configure Swagger
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}");
app.MapFallbackToFile("/index.html");
app.Run();The following screenshot represents loading data when the button is clicked and CRUD operations are performed:

A complete sample for binding data and performing CRUD actions through Fetch requests is available on GitHub.
Display the loading indicator with local data.
The Syncfusion® Vue Grid allows the display of a loading indicator while loading local data. This is useful when data loading from a local source is delayed and a visual indication is required to show that data is being fetched.
To display the loading indicator when using local data, set the showSpinner property to true. This property controls the visibility of the loading indicator.
The following example demonstrates displaying the loading indicator in the Syncfusion® Vue Grid using the load and created events:
<template>
<div id="app">
<ejs-grid ref="grid" :load='load' :created='created' >
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' width=120></e-column>
<e-column field='CustomerID' headerText='Customer ID' textAlign='Right' width=120></e-column>
<e-column field='Quantity' headerText='Quantity' textAlign='Right' width=120></e-column>
<e-column field='CustomerAddress' headerText='Ship Address' textAlign='Right' width=120></e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective,Page} from "@syncfusion/ej2-vue-grids";
import { data } from './datasource.js';
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
},
data() {
return {
isDataLoading :true
};
},
methods: {
load() {
if (this.isDataLoading) {
this.$refs.grid.ej2Instances.showSpinner();
}
},
created(){
this.isDataLoading = true;
this.$refs.grid.ej2Instances.dataSource = data ;
}
},
provide: {
grid: [Page]
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>Binding data and performing CRUD actions via AJAX request
The Syncfusion® Vue 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 Syncfusion® Vue Grid, follow these steps:
Step 1: Include the Syncfusion® Vue Grid in the HTML file with the necessary configurations:
<ejs-button cssClass='e-success' @click="click">Bind data via ajax</ejs-button>
<div style="padding: 20px 17px 0 0">
<ejs-grid ref="grid" :dataSource='data' :editSettings='editSettings' :toolbar='toolbar' allowPaging="true" height="320" :actionBegin="actionBegin" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' isPrimaryKey=true width='150'></e-column>
<e-column field='CustomerID' headerText='Customer Name' width='150'></e-column>
<e-column field='ShipCity' headerText='ShipCity' width='150' textAlign='Right'></e-column>
</e-columns>
</ejs-grid>
</div>
</div>Step 2: To bind data from an external AJAX 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 AJAX request.
click: function () {
const ajax = new Ajax("https://localhost:****/Home/Getdata", 'POST');//Use remote server host number instead ****
ajax.send();
ajax.onSuccess = (data) => {
this.$refs.grid.ej2Instances.dataSource = JSON.parse(data);
};
}On the server side, there is a method named “GetData” within the HomeController that contains the grid’s data source. When the button is clicked, an AJAX request is sent to fetch the data from the server and bind it to the Grid component.
public class HomeController : Controller
{
public ActionResult Getdata()
{
var DataSource = OrdersDetails.GetAllRecords();
return Json(DataSource);
}
}Create a model class named OrdersDetails.cs in the server-side Models folder to represent the order data.
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;
}
//Render data in this method
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; }
}Step 3: To perform CRUD actions, leverage the actionBegin event. The default CRUD operations can be canceled by utilizing the cancel argument provided by this event. This allows dynamic invocation of server-side methods using AJAX, along with the relevant data received from the actionBegin event, to update server data accordingly.
A. To add and save a new record using AJAX requests, follow these steps:
public flag = false;
actionBegin: function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!this.flag) {
// Add and edit operations
if (e.requestType == 'save' && (e.action == 'add')) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using an Ajax call
var ajax = new Ajax({
url: 'https://localhost:****/Home/Insert', //Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
ajax.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
this.flag = true;
//The added/edited data will be saved in the Grid
this.$refs.grid.ej2Instances.endEdit();
};
ajax.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
this.flag = false;
};
ajax.send();
}
}
}//insert the record
public ActionResult Insert([FromBody] CRUDModel<OrdersDetails> value)
{
OrdersDetails.GetAllRecords().Insert(0, value.value);
return Json(value.value);
}B. To edit and save a record using an AJAX request, follow these steps:
public flag = false;
actionBegin: function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!this.flag) {
if (e.requestType == 'save' && (e.action == "edit")) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using an Ajax call
var ajax = new Ajax({
url: 'https://localhost:****/Home/Update', //Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
ajax.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
this.flag = true;
// The added/edited data will be saved in the Grid
this.$refs.grid.ej2Instances.endEdit();
};
ajax.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next executio
this.flag = false;
};
ajax.send();
}
}
}//update the record
public ActionResult Update([FromBody] CRUDModel<OrdersDetails> value)
{
var ord = value.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.value);
}C. To delete a record using an AJAX request, follow these steps:
public flag = false;
actionBegin: function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!this.flag) {
if (e.requestType == 'delete') {
var editedData = e.data;
// The default delete operation is cancelled
e.cancel = true;
// The deleted data can be sent to the server using an Ajax call
var ajax = new Ajax({
url: 'https://localhost:****/Home/Delete', //Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ key: editedData[0][this.$refs.grid.ej2Instances.getPrimaryKeyFieldNames()[0]] })
});
ajax.onSuccess = () => {
// this.flag is enabled to skip this execution when grid deletes record
this.flag = true;
// The deleted data will be removed in the Grid
this.$refs.grid.ej2Instances.deleteRecord();
};
ajax.onFailure = () => {
// Delete failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
this.flag = false;
};
ajax.send();
}
}
}//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 4: In the AJAX success event, the Grid’s endEdit and deleteRecord methods can be utilized 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, a flag variable can be employed and managed within the actionComplete and AJAX failure events. The following code snippet demonstrates this approach:
actionComplete(e) {
if (e.requestType === 'save' || e.requestType === 'delete') {
// The this.flag is disabled after operation is successfully performed so that it can enter the condition on next execution
this.flag = false;
}
}The following example demonstrates data binding and CRUD actions performed through Ajax requests on both the client side and server side.
<template>
<div id="app">
<ejs-button cssClass='e-success' @click="click">Bind data via ajax</ejs-button>
<div style="padding: 20px 17px 0 0">
<ejs-grid ref="grid" :dataSource='data' :editSettings='editSettings' :toolbar='toolbar' allowPaging="true" height="320" :actionBegin="actionBegin" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' isPrimaryKey=true width='150'></e-column>
<e-column field='CustomerID' headerText='Customer Name' width='150'></e-column>
<e-column field='ShipCity' headerText='ShipCity' width='150' textAlign='Right'></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script setup>
import { provide, ref } from "vue";
import { GridComponent as EjsGrid, ColumnDirective as EColumn, ColumnsDirective as EColumns, Page,Edit,Toolbar } from "@syncfusion/ej2-vue-grids";
import { ButtonComponent as EjsButton } from "@syncfusion/ej2-vue-buttons";
import { Ajax } from '@syncfusion/ej2-base';
const grid = ref(null);
let flag = false;
const editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal' };
const toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
const click = function () {
const ajax = new Ajax("https://localhost:****/Home/Getdata", 'POST'); // Use remote server host number instead ****
ajax.send();
ajax.onSuccess = (data) => {
grid.value.ej2Instances.dataSource = data;
};
};
const actionBegin = function (e) {
// Initially this.flag needs to be false in order to enter this condition
if (!flag) {
// Add and edit operations
if (e.requestType === 'save' && (e.action === 'add')) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using an Ajax call.
const a = new Ajax({
url: 'https://localhost:****/Home/Insert',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
}); // Use remote server host number instead ****
ajax.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
flag = true;
// The added/edited data will be saved in the Grid
grid.value.ej2Instances.endEdit();
};
ajax.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
flag = false;
};
ajax.send();
}
if (e.requestType === 'save' && (e.action === "edit")) {
var editedData = e.data;
// The default edit operation is cancelled
e.cancel = true;
// The updated data can be sent to the server using an Ajax call
const Ajax = new Ajax({
url: 'https://localhost:****/Home/Update',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
}); // Use remote server host number instead ****
ajax.onSuccess = () => {
// this.flag is enabled to skip this execution when grid ends add/edit
flag = true;
// The added/edited data will be saved in the Grid
grid.value.ej2Instances.endEdit();
};
ajax.onFailure = () => {
// Add/edit failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
flag = false;
};
ajax.send();
}
if (e.requestType === 'delete') {
var editedData = e.data;
// The default delete operation is cancelled
e.cancel = true;
// The deleted data can be sent to the server using an Ajax call
const ajax = new Ajax({
url: 'https://localhost:****/Home/Delete',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ key: editedData[0][grid.value.ej2Instances.getPrimaryKeyFieldNames()[0]] })
}); // Use remote server host number instead ****
ajax.onSuccess = () => {
// this.flag is enabled to skip this execution when grid deletes record
flag = true;
// The deleted data will be removed in the Grid
grid.value.ej2Instances.deleteRecord();
};
ajax.onFailure = () => {
// Delete failed
// The this.flag is disabled if operation is failed so that it can enter the condition on next execution
flag = false;
};
ajax.send();
}
}
};
const actionComplete = function (e) {
if (e.requestType === 'save' || e.requestType === 'delete') {
// The this.flag is disabled after operation is successfully performed so that it can enter the condition on next execution
flag = false;
}
};
provide('grid', [Page, Edit, Toolbar]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
</style><template>
<div id="app">
<ejs-button cssClass='e-success' @click="click">Bind data via Ajax</ejs-button>
<div style="padding: 20px 17px 0 0">
<ejs-grid ref="grid" :dataSource='data' :editSettings='editSettings' :toolbar='toolbar' allowPaging="true" height="320" :actionBegin="actionBegin" :actionComplete="actionComplete">
<e-columns>
<e-column field='OrderID' headerText='Order ID' isPrimaryKey=true width='150'></e-column>
<e-column field='CustomerID' headerText='Customer Name' width='150'></e-column>
<e-column field='ShipCity' headerText='ShipCity' width='150' textAlign='Right'></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective, Page, Edit, Toolbar } from "@syncfusion/ej2-vue-grids";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import {Ajax } from '@syncfusion/ej2-base';
export default {
name: "App",
components: {
"ejs-grid": GridComponent,
"e-columns": ColumnsDirective,
"e-column": ColumnDirective,
"ejs-button": ButtonComponent
},
data() {
return {
flag: false,
editSettings: { allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Normal' },
toolbar: ['Add', 'Edit', 'Delete', 'Update', 'Cancel']
};
},
methods: {
click: function () {
const ajax = new Ajax("https://localhost:****/Home/Getdata", 'POST');//Use remote server host number instead ****
ajax.send();
ajax.onSuccess = (data) => {
this.$refs.grid.ej2Instances.dataSource = JSON.parse(data);
};
},
actionBegin: function (e) {
if (!this.flag) {
if (e.requestType == 'save' && (e.action == 'add')) {
var editedData = e.data;
e.cancel = true;
var ajax = new Ajax({
url: 'https://localhost:****/Home/Insert',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
ajax.onSuccess = () => {
this.flag = true;
this.$refs.grid.ej2Instances.endEdit();
};
ajax.onFailure = () => {
this.flag = false;
};
ajax.send();
}
if (e.requestType == 'save' && (e.action == "edit")) {
var editedData = e.data;
e.cancel = true;
var Ajax = new Ajax({
url: 'https://localhost:****/Home/Update',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ value: editedData })
});
ajax.onSuccess = () => {
this.flag = true;
this.$refs.grid.ej2Instances.endEdit();
};
ajax.onFailure = () => {
this.flag = false;
};
ajax.send();
}
if (e.requestType == 'delete') {
var editedData = e.data;
e.cancel = true;
var ajax = new Ajax({
url: 'https://localhost:****/Home/Delete',//Use remote server host number instead ****
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({ key: editedData[0][this.$refs.grid.ej2Instances.getPrimaryKeyFieldNames()[0]] })
});
ajax.onSuccess = () => {
this.flag = true;
this.$refs.grid.ej2Instances.deleteRecord();
};
ajax.onFailure = () => {
this.flag = false;
};
a.send();
}
}
},
actionComplete: function (e) {
if (e.requestType === 'save' || e.requestType === 'delete') {
this.flag = false;
}
}
},
provide: {
grid: [Page, Edit, Toolbar]
},
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>using FetchProject.Server.Models;
using Microsoft.AspNetCore.Mvc;
namespace FetchProject.Server.Controllers
{
public class HomeController : Controller
{
public ActionResult Getdata()
{
var DataSource = OrdersDetails.GetAllRecords();
return Json(DataSource);
}
public ActionResult Update([FromBody] CRUDModel<OrdersDetails> value)
{
var ord = value.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.value);
}
//insert the record
public ActionResult Insert([FromBody] CRUDModel<OrdersDetails> value)
{
OrdersDetails.GetAllRecords().Insert(0, value.value);
return Json(value.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);
}
}
}
public class CRUDModel<T> where T : class
{
public string action { get; set; }
public string table { 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; }
}namespace FetchProject.Server.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()
{
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; }
}
}var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
});
});
var app = builder.Build();
// Configure HTTPS redirection
app.UseHttpsRedirection();
// Configure CORS
app.UseCors();
app.UseDefaultFiles();
app.UseStaticFiles();
// Configure Swagger
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}");
app.MapFallbackToFile("/index.html");
app.Run();The following screenshot represents loading data when the button is clicked and CRUD operations are performed:

A complete sample for binding data and performing CRUD actions using AJAX requests is available on GitHub.
Display the loading indicator using AJAX.
The Syncfusion® Vue Grid allows the display of a loading indicator while loading data using AJAX. This feature is useful when there is a delay in loading data from a data source, and a visual indication is required to show that the data is being fetched. This is particularly beneficial when working with large datasets or under conditions of slower internet connections.
Set the showSpinner property to true to display the loading indicator. This property controls the spinner visibility.
The following example demonstrates displaying the loading indicator in the Syncfusion® Vue Grid using the load and created events:
<template>
<div id="app">
<ejs-grid ref="grid" :load='load' :created='created' >
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' width=120></e-column>
<e-column field='CustomerID' headerText='Customer ID' textAlign='Right' width=120></e-column>
<e-column field='Quantity' headerText='Quantity' textAlign='Right' width=120></e-column>
<e-column field='CustomerAddress' headerText='Ship Address' textAlign='Right' width=120></e-column>
</e-columns>
</ejs-grid>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective,Page} from "@syncfusion/ej2-vue-grids";
import { Ajax } from "@syncfusion/ej2-base";
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
},
data() {
return {
isDataLoading :true
};
},
methods: {
load() {
if (this.isDataLoading) {
this.$refs.grid.ej2Instances.showSpinner();
this.isDataLoading = false;
}
},
created() {
this.isDataLoading = true;
const grid = this.$refs.grid.ej2Instances; // Grid instance
const ajax = new Ajax(
'https://services.syncfusion.com/vue/production/api/orders',
'GET'
);
ajax.send();
ajax.onSuccess = (data) => {
grid.dataSource = JSON.parse(data);
};
}
},
provide: {
grid: [Page]
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>Managing spinner visibility during data loading
Showing a spinner during data loading in the Syncfusion® vue 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, use the showSpinner and hideSpinner methods provided by the Grid component.
The following example demonstrates showing and hiding the spinner during data loading using external buttons in a grid:
<template>
<div id="app">
<div>
<div>
<ejs-button cssClass="e-outline" v-on:click="onloadData">Load Data</ejs-button>
<ejs-button style='margin-left:5px' cssClass="e-outline" v-on:click="showhideSpinner">Show Spinner</ejs-button>
<ejs-button style='margin-left:5px' cssClass="e-outline" v-on:click="showhideSpinner">Hide Spinner</ejs-button>
</div>
<ejs-grid ref="grid" allowPaging='true' height='255px' style='margin-top:10px'>
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
<e-column field='CustomerID' headerText='Customer ID' width=100></e-column>
<e-column field='ProductName' headerText='Product Name' width=110></e-column>
<e-column field='Quantity' headerText='Quantity' width=100></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script setup>
import { provide,ref } from "vue";
import { GridComponent as EjsGrid, ColumnDirective as EColumn, ColumnsDirective as EColumns, Page } from "@syncfusion/ej2-vue-grids";
import { ButtonComponent as EjsButton } from "@syncfusion/ej2-vue-buttons";
import { data } from './datasource.js';
const grid=ref(null);
const onloadData=function(){
grid.value.ej2Instances.showSpinner();
setTimeout(() => {
grid.value.ej2Instances.dataSource = data;
grid.value.hideSpinner();
}, 1000);
};
const showhideSpinner=function(args){
if(args.currentTarget.innerText === 'Show Spinner'){
grid.value.showSpinner();
}
else{
grid.value.hideSpinner();
}
}
provide('grid', [Page]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style><template>
<div id="app">
<div>
<div>
<ejs-button cssClass="e-outline" v-on:click="onloadData">Load Data</ejs-button>
<ejs-button style='margin-left:5px' cssClass="e-outline" v-on:click="showhideSpinner">Show Spinner</ejs-button>
<ejs-button style='margin-left:5px' cssClass="e-outline" v-on:click="showhideSpinner">Hide Spinner</ejs-button>
</div>
<ejs-grid ref="grid" allowPaging='true' height='255px' style='margin-top:10px'>
<e-columns>
<e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
<e-column field='CustomerID' headerText='Customer ID' width=100></e-column>
<e-column field='ProductName' headerText='Product Name' width=110></e-column>
<e-column field='Quantity' headerText='Quantity' width=100></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective,Page} from "@syncfusion/ej2-vue-grids";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import { data } from './datasource.js';
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
"ejs-button": ButtonComponent
},
data() {
return {
dataGrid:data,
};
},
methods: {
onloadData(){
this.$refs.grid.ej2Instances.showSpinner();
setTimeout(() => {
this.$refs.grid.ej2Instances.dataSource = data;
this.$refs.grid.ej2Instances.hideSpinner();
}, 1000);
},
showhideSpinner(args){
if(args.currentTarget.innerText === 'Show Spinner'){
this.$refs.grid.ej2Instances.showSpinner();
}
else{
this.$refs.grid.ej2Instances.hideSpinner();
}
}
},
provide: {
grid: [Page]
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
</style>Immutable mode
Immutable mode in the Syncfusion® Vue 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.
Enable immutable mode by setting enableImmutableMode to true.
If immutable mode is enabled, when the datasource changes, only newly added rows are regenerated or reused. Consequently, the grid’s 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. Therefore, the isPrimaryKey column.
The following example demonstrates enabling immutable mode in an Vue 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:
<template>
<div id="app">
<ejs-button class="e-control e-btn e-lib e-info" @click="addTopEvent">Add rows Data</ejs-button>
<ejs-button style="margin-left: 20px" class="e-control e-btn e-lib e-info" @click="deleteEvent">Delete rows</ejs-button>
<ejs-button style="margin-left: 20px" class="e-control e-btn e-lib e-info" @click="updateEvent">Update Freight Data</ejs-button>
<div id="message">{{ message }}</div>
<div style="padding-top: 5px">
<ejs-grid ref="immutablegrid" :dataSource="rowData" :enableHover="false" height="235" :enableImmutableMode="true" allowPaging="true" :selectionSettings="selectionOptions" :rowDataBound="rowDataBound" :queryCellInfo="queryCellInfo">
<e-columns>
<e-column field="OrderID" headerText="Order ID" isPrimaryKey="true" width="120" textAlign="Right"></e-column>
<e-column field="CustomerID" headerText="Customer ID" width="120"></e-column>
<e-column field="Freight" headerText="Freight" width="120"></e-column>
<e-column field="ShipName" headerText="Ship Name" width="120"></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script setup>
import { provide, ref } from "vue";
import { GridComponent as EjsGrid, ColumnDirective as EColumn, ColumnsDirective as EColumns, Page } from "@syncfusion/ej2-vue-grids";
import { ButtonComponent as EjsButton } from "@syncfusion/ej2-vue-buttons";
import { data } from './datasource.js';
const immutablegrid = ref(null);
const rowData = ref(data);
const selectionOptions = { type: 'Multiple' };
const message = ref(`Initial rows rendered: ${data.length}`);
const queryCellInfo = (args) => {
if (args.column.field === 'ShipName' && args.data.ShipName === "Gems Chevalier") {
args.cell.style.backgroundColor = 'rgb(210, 226, 129)';
}
};
const rowDataBound = (args) => {
args.row.style.backgroundColor = args.data.isNewlyAdded ? '' : 'rgb(208, 255, 255)';
};
const addTopEvent = () => {
immutablegrid.value.ej2Instances.getAllDataRows().forEach(row => {
row.style.backgroundColor = "rgb(208, 255, 255)";
});
let newRowData = [];
let addedRecords = {
'OrderID': generateOrderId(),
'CustomerID': generateCustomerId(),
'ShipCity': generateShipCity(),
'Freight': generateFreight(),
'ShipName': generateShipName(),
'isNewlyAdded': true
};
newRowData.push(addedRecords);
rowData.value = [...newRowData, ...rowData.value];
message.value = `${newRowData.length} rows rendered after performing the add action`;
};
const deleteEvent = () => {
if (rowData.value.length > 0) {
rowData.value = rowData.value.slice(1);
message.value = `1 row deleted after performing delete action`;
} else {
message.value = `No rows to delete`;
}
};
const updateEvent = () => {
let count = 0;
let newRowData = rowData.value.map((row) => {
if (row.ShipName === 'Hanari Carnes') {
count++;
return { ...row, 'ShipName': "Gems Chevalier" };
} else {
return row;
}
});
rowData.value = newRowData;
message.value = `${count} rows updated after performing update action`;
};
const generateOrderId = () => {
return Math.floor(10000 + Math.random() * 90000);
};
const generateCustomerId = () => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let result = '';
for (let i = 0; i < 5; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
};
const generateShipCity = () => {
const cities = ['London', 'Paris', 'New York', 'Tokyo', 'Berlin'];
return cities[Math.floor(Math.random() * cities.length)];
};
const generateFreight = () => {
const randomValue = Math.random() * 100;
return parseFloat(randomValue.toFixed(2));
};
const generateShipName = () => {
const names = ['Que Delícia', 'Hanari Carnes', 'Island Trading', 'Laughing Bacchus Winecellars'];
return names[Math.floor(Math.random() * names.length)];
};
provide('grid', [Page]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
#message{
color:red;
text-align: center;
padding-top: 10px;
}
</style><template>
<div id="app">
<ejs-button class="e-control e-btn e-lib e-info" @click="addTopEvent">Add rows Data</ejs-button>
<ejs-button style="margin-left: 20px" class="e-control e-btn e-lib e-info" @click="deleteEvent">Delete rows</ejs-button>
<ejs-button style="margin-left: 20px" class="e-control e-btn e-lib e-info" @click="updateEvent">Update Freight Data</ejs-button>
<div id="message">{{ message }} </div>
<div style="padding-top: 5px">
<ejs-grid ref="immutable" :dataSource='rowData' :enableHover="false" height='235' :enableImmutableMode="true" allowPaging="true" :selectionSettings="selectionOptions"
:rowDataBound="rowDataBound" :queryCellInfo="queryCellInfo">
<e-columns>
<e-column field='OrderID' headerText='Order ID' isPrimaryKey="true" width='120'
textAlign='Right'></e-column>
<e-column field='CustomerID' headerText='Customer ID' width='120'></e-column>
<e-column field='Freight' headerText='Freight' width='120'></e-column>
<e-column field='ShipName' headerText='Ship Name' width='120'></e-column>
</e-columns>
</ejs-grid>
</div>
</div>
</template>
<script>
import { GridComponent, ColumnsDirective, ColumnDirective,Page} from "@syncfusion/ej2-vue-grids";
import { ButtonComponent } from "@syncfusion/ej2-vue-buttons";
import { data } from './datasource.js';
export default {
name: "App",
components: {
"ejs-grid":GridComponent,
"e-columns":ColumnsDirective,
"e-column":ColumnDirective,
"ejs-button": ButtonComponent
},
data() {
return {
rowData : data,
selectionOptions : { type: 'Multiple' },
message : `Initial rows rendered: ${data.length}`
};
},
methods: {
queryCellInfo(args){
if (args.column.field === 'ShipName' && args.data.ShipName === "Gems Chevalier") {
args.cell.style.backgroundColor = 'rgb(210, 226, 129)';
}
},
rowDataBound(args){
args.row.style.backgroundColor = args.data.isNewlyAdded ? '' : ' rgb(208, 255, 255)';
},
addTopEvent(){
this.$refs.immutable.ej2Instances.getAllDataRows().forEach(row => {
row.style.backgroundColor = "rgb(208, 255, 255)";
});
let count = 0;
if (count < 1) {
let newRowData = [];
let addedRecords = {
'OrderID': this.generateOrderId(),
'CustomerID': this.generateCustomerId(),
'ShipCity': this.generateShipCity(),
'Freight': this.generateFreight(),
'ShipName': this.generateShipName(),
'isNewlyAdded': true
};
newRowData.push(addedRecords);
this.rowData = ([...newRowData, ...this.rowData]);
count++;
this.message = `${count} rows rendered after performing the add action`;
}
},
deleteEvent() {
let count = 0;
if (count < 1 && (this.rowData).length > 0) {
this.rowData = (this.rowData).slice(1);
count++;
this.message = `${count} rows deleted after performing delete action`;
}
},
updateEvent(){
let count = 0;
let newRowData = (this.rowData).map((row) => {
if (row.ShipName === 'Hanari Carnes') {
count++;
return { ...row, 'ShipName': "Gems Chevalier" };
} else {
return row;
}
});
this.rowData = newRowData;
this.message = ` ${count} rows updated after performing update action`;
},
generateOrderId(){
return Math.floor(10000 + Math.random() * 90000);
},
generateCustomerId(){
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let result = '';
for (let i = 0; i < 5; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
},
generateShipCity() {
const cities = ['London', 'Paris', 'New York', 'Tokyo', 'Berlin'];
return cities[Math.floor(Math.random() * cities.length)];
},
generateFreight(){
const randomValue = Math.random() * 100;
return parseFloat(randomValue.toFixed(2));
},
generateShipName(){
const names = ['Que Delícia', 'Hanari Carnes', 'Island Trading', 'Laughing Bacchus Winecellars'];
return names[Math.floor(Math.random() * names.length)];
}
},
provide: {
grid: [Page]
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/material.css";
#message{
color:red;
text-align: center;
padding-top: 10px;
}
</style>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