Advanced scenarios in Syncfusion EJ2 JavaScript DataManager
21 Jul 202524 minutes to read
This section covers advanced features such as offline mode, load on demand, deferred execution, and error handling in the Syncfusion EJ2 JavaScript DataManager.
Offline mode
Offline mode in Syncfusion EJ2 JavaScript DataManager enables full client-side data processing by fetching data from the server once and then performing all subsequent operations (such as filtering, sorting, paging, and grouping) locally, without additional network requests.
This feature is ideal for:
-
Applications with static or infrequently changing datasets.
-
Reducing repeated server calls to enhance performance.
-
Supporting offline first workflows where a persistent internet connection is not guaranteed.
You can enable this feature by setting the offline property to true when creating the DataManager instance. When the offline property is set to true, the DataManager fetches data from the server once and then performs all subsequent operations on the locally stored data, ensuring faster and responsive UI interactions without further server round-trips.
In remote data binding, each call to executeQuery sends a request to the server for processing to avoid repeated server postbacks, you can set the DataManager to load all data during initialization and handle query processing entirely on the client-side.
The following sample demonstrates how to enable offline mode:
var template = '<tr><td>${OrderID}</td><td>${CustomerID}</td><td>${EmployeeID}</td></tr>';
var compiledFunction = ej.base.compile(template);
const SERVICE_URI = 'https://services.odata.org/V4/Northwind/Northwind.svc/Orders/?$top=7';
var table = (document.getElementById('datatable'));
var datamanager = new ej.data.DataManager({
url: SERVICE_URI,
adaptor: new ej.data.ODataV4Adaptor,
offline: true
});
datamanager.ready.then((e) => {
(e.result).forEach((data) => {
table.appendChild(compiledFunction(data)[0]);
});
});<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 DataManager</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript DataManager Control">
<meta name="author" content="Syncfusion">
<script src="https://cdn.syncfusion.com/ej2/31.2.12/dist/ej2.min.js" type="text/javascript"></script>
<style>
.e-table {
border: solid 1px #e0e0e0;
border-collapse: collapse;
font-family: Roboto;
}
.e-table td,
.e-table th {
border-style: solid;
border-width: 1px 0 0;
border-color: #e0e0e0;
display: table-cell;
font-size: 14px;
line-height: 20px;
overflow: hidden;
padding: 8px 21px;
vertical-align: middle;
white-space: nowrap;
width: auto;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='container'>
<table id='datatable' class='e-table'>
<thead>
<tr>
<th>Order ID</th>
<th>Customer ID</th>
<th>Employee ID</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<script>
var ele = document.getElementById('container');
if (ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body>
</html>Load on demand
Load on demand is an efficient technique that optimizes performance and reduces bandwidth usage by fetching only a specific subset of data from the server, rather than loading the entire dataset at once. This approach is particularly beneficial for applications dealing with large datasets, ensuring faster load times and improved responsiveness.
You can achieve load on demand using the Query.page method in Syncfusion EJ2 JavaScript DataManager. This method requests a specific page of data from the server, based on the given page number and page size.
For example, an employee directory in an HR portal displays thousands of records. Instead of loading all employees at once, the Grid uses Query.page to retrieve only the records for the current page, loading data on demand as users navigate through pages.
The following code example demonstrates the implementation of load on demand using DataManager:
let template ='<tr><td>${EmployeeID }</td><td>${Employees}</td><td>${Designation}</td><td>${Location}</td><td>${Status}</td></tr>';
let table = document.getElementById('datatable');
let compiledFunction = ej.base.compile(template);
const dataManager = new ej.data.DataManager({
url: "https://services.syncfusion.com/js/production/api/UrlDataSource",
adaptor: new ej.data.UrlAdaptor(),
crossDomain: true
});
const btnLoad = document.getElementById("submit");
const inputFrom = document.getElementById("pageIndex");
const inputTo = document.getElementById("pageSize");
// Initial load: page 1, size 5.
let query = new ej.data.Query().page(1, 5);
function renderTable(data) {
data.forEach((data) => {
table.appendChild(compiledFunction(data)[0]);
});
}
// Execute initial query.
dataManager.executeQuery(query).then((e) => {
renderTable(e.result);
}).catch(console.error);
// Load data on demand button click.
btnLoad.addEventListener("click", () => {
const pageIndex = parseInt(inputFrom.value);
const pageSize = parseInt(inputTo.value);
if (!isNaN(pageIndex) && !isNaN(pageSize) && pageIndex > 0 && pageSize > 0) {
const tempQuery = new ej.data.Query().page(pageIndex, pageSize);
dataManager.executeQuery(tempQuery).then((e) => {
renderTable(e.result);
}).catch(console.error);
}
});<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 DataManager</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript DataManager Control">
<meta name="author" content="Syncfusion">
<link href="index.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.syncfusion.com/ej2/31.2.12/dist/ej2.min.js" type="text/javascript"></script>
<script src="es5-datasource.js" type="text/javascript"></script>
<style>
body {
margin-top: 40px;
font-family: Roboto, sans-serif;
}
#container {
max-width: 1000px;
margin: auto;
}
table.e-table {
width: 100%;
border-collapse: collapse;
border: 1px solid #e0e0e0;
}
table.e-table th,
table.e-table td {
border: 1px solid #e0e0e0;
padding: 10px;
text-align: left;
white-space: nowrap;
}
table.e-table th {
background-color: #343a40;
color: white;
}
.form-section {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 20px;
border-bottom: 1px solid #ccc;
padding-bottom: 12px;
}
.form-group {
display: flex;
align-items: center;
gap: 8px;
}
.form-group label {
min-width: 100px;
font-weight: 600;
white-space: nowrap;
}
.form-group input {
width: 120px;
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
}
.btn-primary {
background-color: #007bff;
color: white;
border: none;
padding: 8px 6px;
font-size: 14px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s ease;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-primary:active {
background-color: #004494;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id="container">
<div class="form-section">
<div class="form-group">
<label for="pageIndex">Page Index:</label>
<input type="text" id="pageIndex" class="form-control" placeholder="e.g. 1">
</div>
<div class="form-group">
<label for="pageSize">Page Size:</label>
<input type="text" id="pageSize" class="form-control" placeholder="e.g. 5">
</div>
<button id="submit" class="btn btn-primary">Load data on demand</button>
</div>
<table id="datatable" class="e-table">
<thead>
<tr>
<th>Employee ID</th>
<th>Employee Name</th>
<th>Designation</th>
<th>Location</th>
<th>Status</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script>
var ele = document.getElementById('container');
if (ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body>
</html>Deferred execution & error handling
Deferred execution & error handling is a technique used in asynchronous programming to manage operations that take time to complete, such as network requests, loading data from a server or file access.
This approach improves code readability, maintainability, and robustness by separating the logic for successful execution (then, resolve) from error handling (catch, reject). It enables chaining of asynchronous tasks and ensures that errors are caught and managed gracefully, preventing unexpected application crashes.
To achieve this, the following concepts are used:
-
catch- Handles errors or rejections that occur during the asynchronous operation.
-
promise- Represents the eventual completion (or failure) of an asynchronous operation and its resulting value. If it fails, it passes an error or reason to the rejection handler for appropriate processing.
-
reject- Indicates that the asynchronous operation has failed and passes an error or reason to the handler.
-
resolve- Indicates that the asynchronous operation has completed successfully and passes the result to the handler.
-
then- Executes the logic that run after the asynchronous operation completes successfully. It allows chaining of further actions.
The following sample demonstrates how to use deferred execution & error handling:
const SERVICE_URI = 'https://services.odata.org/V4/Northwind/Northwind.svc/Orders/';
const CUSTOMER_URI = 'https://services.odata.org/V4/Northwind/Northwind.svc/Customers/';
const table = document.querySelector('#datatable');
const tbody = table.querySelector('tbody');
const messageDiv = document.getElementById('message');
let switchObj = new ej.buttons.Switch({
cssClass: 'handle-text',
change: change
});
switchObj.appendTo('#switch1');
switchObj.toggle();
function fetchDataWithAdaptor(url, adaptor, deferred){
const manager = new ej.data.DataManager({
url,
adaptor,
crossDomain: true
});
manager.executeQuery(new ej.data.Query())
.then((response) => {
if ('result' in response) {
deferred.resolve(response.result);
}
})
.catch((error) => {
deferred.reject(error);
});
}
function handleResult(deferred) {
deferred.promise
.then((result) => {
tbody.innerHTML = '';
result.forEach((item) => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.OrderID}</td>
<td>${item.CustomerID}</td>
<td>${item.EmployeeID}</td>
`;
tbody.appendChild(row);
});
messageDiv.innerText = '✅ Data loaded successfully';
messageDiv.style.color = 'green';
})
.catch(() => {
tbody.innerHTML = '';
messageDiv.innerText = '❌ Error loading data';
messageDiv.style.color = 'red';
});
}
// Initial load.
const initialDeferred = new ej.data.Deferred();
handleResult(initialDeferred);
fetchDataWithAdaptor(SERVICE_URI, new ej.data.ODataV4Adaptor(), initialDeferred);
function change(args) {
const newDeferred = new ej.data.Deferred();
const useCustomer = switchObj.checked;
const newUrl = useCustomer ? SERVICE_URI : CUSTOMER_URI;
const newAdaptor = useCustomer ? new ej.data.ODataV4Adaptor() : new ej.data.UrlAdaptor();
handleResult(newDeferred);
fetchDataWithAdaptor(newUrl, newAdaptor, newDeferred);
}<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 Grid</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Typescript Grid Control">
<meta name="author" content="Syncfusion">
<link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-base/styles/fabric.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-buttons/styles/bootstrap.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="https://cdn.syncfusion.com/ej2/31.2.12/dist/ej2.min.js" type="text/javascript"></script>
<style>
#loadData {
background-color: #007bff;
color: white;
border: none;
padding: 10px 20px;
font-size: 14px;
border-radius: 4px;
cursor: pointer;
margin: 20px 60px;
font-family: Roboto, sans-serif;
transition: background-color 0.3s ease;
}
#loadData:hover {
background-color: #0056b3;
}
#datatable {
width: 90%;
margin: 0 auto;
border-collapse: collapse;
font-family: Roboto, sans-serif;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
#datatable thead {
background-color: #007bff;
color: white;
}
#datatable th,
#datatable td {
padding: 12px 15px;
border: 1px solid #ddd;
text-align: left;
}
#datatable tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
#datatable tbody tr:hover {
background-color: #f1f1f1;
}
#message {
text-align: center;
margin-left: 60px;
color: red;
padding-bottom: 20px;
}
/* Customize Handle and Bar Switch */
.e-switch-wrapper.handle-text {
width: 200px;
height: 30px;
}
.e-switch-wrapper.handle-text .e-switch-handle {
width: 90px;
height: 20px;
left: 10px;
background-color: #fff;
}
.e-switch-wrapper.handle-text .e-switch-inner,
.e-switch-wrapper.handle-text .e-switch-handle {
border-radius: 0;
}
.e-switch-wrapper.handle-text .e-switch-handle.e-switch-active {
left: 125px;
}
.e-switch-wrapper.handle-text .e-switch-inner.e-switch-active,
.e-switch-wrapper.handle-text:hover .e-switch-inner.e-switch-active .e-switch-on {
background-color: #4d841d;
border-color: #4d841d;
}
.e-switch-wrapper.handle-text .e-switch-inner,
.e-switch-wrapper.handle-text .e-switch-off {
background-color: #e3165b;
border-color: #e3165b;
}
.e-switch-wrapper.handle-text .e-switch-inner:after,
.e-switch-wrapper.handle-text .e-switch-inner:before {
font-size: 10px;
position: absolute;
line-height: 21px;
font-family: "Helvetica", sans-serif;
z-index: 1;
height: 100%;
transition: all 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
}
.e-switch-wrapper.handle-text .e-switch-inner:before {
content: "UrlAdaptor";
padding-top: 4px;
color: #e3165b;
left: 12px;
}
.e-switch-wrapper.handle-text .e-switch-inner:after {
content: "ODataV4Adaptor";
right: 10px;
color: #fff;
padding-top: 4px;
}
.e-switch-wrapper.handle-text .e-switch-inner.e-switch-active:before {
color: #fff;
}
.e-switch-wrapper.handle-text .e-switch-inner.e-switch-active:after {
color: #4d841d;
}
.e-switch-wrapper.handle-text:not(.e-switch-disabled):hover .e-switch-handle:not(.e-switch-active) {
background-color: #fff;
}
.switch-container {
display: flex;
align-items: center;
gap: 10px;
/* space between switch and label */
margin: 20px 60px;
font-family: Roboto, sans-serif;
}
.switch-label {
font-size: 14px;
}
</style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body style="margin-top: 100px;">
<div id='container'>
<div style="display: flex;align-items: center;margin: 20px 60px; gap: 10px;font-weight:bold">
<span class="switch-label">Toggle Data</span>
<input type="checkbox" id="switch1" />
</div>
<div id='message'></div>
<table id='datatable' class='e-table'>
<thead>
<tr>
<th>Order ID</th>
<th>Customer ID</th>
<th>Employee ID</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<script>
var ele = document.getElementById('container');
if (ele) {
ele.style.visibility = "visible";
}
</script>
<script src="index.js" type="text/javascript"></script>
</body>
</html>