Skip to content

Commit 9040360

Browse files
authored
Merge pull request #1 from SyncfusionExamples/EJ2-898669-crud-github
898669: GitHub sample of remote data topic
2 parents 455f7af + bf0f2c2 commit 9040360

14 files changed

+500
-0
lines changed

.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
# vue-data-grid-integration-with-custom-binding
22
The repository contains a professional demonstration of a custom binding with the Grid control. This application showcases essential functionalities such as filtering, searching, grouping, editing, and paging, all implemented using custom binding.
3+
* Execute the following command to install the necessary dependencies:
4+
`npm install`
5+
* Update the port number in the `serve.js` file to load your data. Also, update the same port number in the `src/orderService.ts` file.
6+
* Run the project using following command:
7+
`npm run start`
8+
Finally, the Syncfusion Vue Grid control will be rendered with custom binding.
9+
![Grid with Custom Binding](image/custom-binding-grid-action.gif)

image/custom-binding-grid-action.gif

351 KB
Loading

index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + Vue</title>
8+
</head>
9+
<body>
10+
<div id="app"></div>
11+
<script type="module" src="/src/main.js"></script>
12+
</body>
13+
</html>

package.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "vite-js-cb",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"start-server": "node server.js",
8+
"start-client": "vite",
9+
"start": "concurrently \"npm run start-server\" \"npm run start-client\""
10+
},
11+
"dependencies": {
12+
"@syncfusion/ej2-data": "^26.2.5",
13+
"@syncfusion/ej2-vue-grids": "^26.2.8",
14+
"axios": "^1.7.3",
15+
"body-parser": "^1.20.2",
16+
"cors": "^2.8.5",
17+
"express": "^4.19.2",
18+
"vue": "^3.4.31"
19+
},
20+
"devDependencies": {
21+
"@vitejs/plugin-vue": "^5.0.5",
22+
"concurrently": "^8.2.2",
23+
"vite": "^5.3.4"
24+
}
25+
}

public/vite.svg

+1
Loading

server.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import express from 'express';
2+
import data from './src/data-source.js';
3+
import bodyParser from 'body-parser';
4+
import cors from 'cors';
5+
6+
const app = express();
7+
8+
app.use(cors({
9+
origin: '*', // Update to the correct frontend origin
10+
credentials: true
11+
}));
12+
13+
app.use(bodyParser.json());
14+
15+
// Root route
16+
app.get('/', (req, res) => {
17+
res.send('Server is running');
18+
});
19+
20+
// all records
21+
app.get('/orders', function (req, res) {
22+
res.json({ result: data, count:data.length });
23+
});
24+
25+
// insert
26+
app.post('/orders', function (req, res){
27+
data.splice(0, 0, req.body.value);
28+
res.status(200).send('Row Inserted');
29+
});
30+
31+
// get specific records
32+
app.get('/orders/:OrderID', (req, res) => {
33+
const orderID = parseInt(req.params.OrderID, 10);
34+
const order = data.find(d => d.OrderID === orderID);
35+
36+
if (order) {
37+
res.json(order);
38+
} else {
39+
res.status(404).send('Order not found');
40+
}
41+
});
42+
// Remove
43+
app.delete('/orders/:OrderID', function (req, res) {
44+
const orderID = parseInt(req.params.OrderID, 10);
45+
const index = data.findIndex(x => x.OrderID === orderID);
46+
if (index !== -1) {
47+
data.splice(index, 1);
48+
res.status(200).send('Row Deleted');
49+
} else {
50+
res.status(404).send('Order not found');
51+
}
52+
});
53+
54+
// Update
55+
app.put('/orders/:OrderID', function (req, res) {
56+
const orderID = parseInt(req.params.OrderID, 10);
57+
const index = data.findIndex(x => x.OrderID === orderID);
58+
if (index !== -1) {
59+
// Assuming req.body.value contains the updated record with the same OrderID
60+
data[index] = req.body.value;
61+
res.status(200).send('Row Updated');
62+
} else {
63+
res.status(404).send('Order not found');
64+
}
65+
});
66+
67+
const port = xxxx; // Here xxxx denotes the port number.
68+
app.listen(port, () => {
69+
console.log(`Server running on http://localhost:${port}`);
70+
});

src/App.vue

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<script setup>
2+
import { ref, onMounted, provide } from "vue";
3+
import { GridComponent as EjsGrid, ColumnsDirective as EColumns, ColumnDirective as EColumn, Page, Sort, Filter, Group, LazyLoadGroup, Toolbar, Edit, Search, } from '@syncfusion/ej2-vue-grids';
4+
import { getOrders, addRecord, updateRecord, deleteRecord } from './orderService';
5+
6+
const data = ref([]);
7+
const grid = ref([]);
8+
const groupSettings = { enableLazyLoading: true, columns: ['ProductName'], showGroupedColumn: true, };
9+
const filterSettings = { columns: [{ field: 'CustomerName', matchCase: false, operator: 'startswith', predicate: 'and', value: 'Maria' }] };
10+
const sortSettings = { columns: [{ field: 'ProductID', direction: 'Descending' }] }
11+
const state = { skip: 0, take: 12, group: groupSettings, sort: sortSettings, filter: filterSettings };
12+
const editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true, newRowPosition: 'Top', };
13+
const toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel', 'Search'];
14+
const OrderIDRules = { required: true };
15+
16+
const dataStateChange = function (state) {
17+
const query = grid.value.getDataModule().generateQuery();
18+
getOrders(state, query).then(gridData => {
19+
data.value = gridData.result; // Assign the result to the data property
20+
});
21+
}
22+
23+
const dataSourceChanged = function (state) {
24+
if (state.action === 'add') {
25+
addRecord(state.data, state); state.endEdit();
26+
} else if (state.action === 'edit') {
27+
updateRecord(state.data, state); state.endEdit();
28+
} else if (state.requestType === 'delete') {
29+
deleteRecord(state.data[0].OrderID, state); state.endEdit();
30+
}
31+
}
32+
33+
onMounted(() => {
34+
dataStateChange(state);
35+
});
36+
37+
provide('grid', [Page, Filter, Sort, Group, LazyLoadGroup, Toolbar, Edit, Search]);
38+
</script>
39+
40+
<template>
41+
<ejs-grid ref="grid" :dataSource='data' :allowPaging='true' :allowFiltering='true' :filterSettings='filterSettings'
42+
:allowSorting='true' :sortSettings='sortSettings' :allowGrouping='true' :groupSettings='groupSettings'
43+
:toolbar='toolbar' :editSettings='editSettings' :dataStateChange="dataStateChange"
44+
:dataSourceChanged="dataSourceChanged">
45+
<e-columns>
46+
<e-column field='OrderID' headerText='Order ID' width='90' textAlign='Right' isPrimaryKey='true'
47+
:validationRules="OrderIDRules"></e-column>
48+
<e-column field="CustomerName" headerText="Customer Name" width="100"></e-column>
49+
<e-column field='ProductID' headerText='Product ID' width=100></e-column>
50+
<e-column field='ProductName' headerText='Product Name' format='C2' width=100></e-column>
51+
</e-columns>
52+
</ejs-grid>
53+
</template>
54+
55+
<style>
56+
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind.css";
57+
@import "../node_modules/@syncfusion/ej2-buttons/styles/tailwind.css";
58+
@import "../node_modules/@syncfusion/ej2-calendars/styles/tailwind.css";
59+
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/tailwind.css";
60+
@import "../node_modules/@syncfusion/ej2-inputs/styles/tailwind.css";
61+
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind.css";
62+
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind.css";
63+
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind.css";
64+
@import "../node_modules/@syncfusion/ej2-vue-grids/styles/tailwind.css";
65+
</style>

src/assets/vue.svg

+1
Loading

src/components/HelloWorld.vue

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script setup>
2+
import { ref } from 'vue'
3+
4+
defineProps({
5+
msg: String,
6+
})
7+
8+
const count = ref(0)
9+
</script>
10+
11+
<template>
12+
<h1>{{ msg }}</h1>
13+
14+
<div class="card">
15+
<button type="button" @click="count++">count is {{ count }}</button>
16+
<p>
17+
Edit
18+
<code>components/HelloWorld.vue</code> to test HMR
19+
</p>
20+
</div>
21+
22+
<p>
23+
Check out
24+
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
25+
>create-vue</a
26+
>, the official Vue + Vite starter
27+
</p>
28+
<p>
29+
Learn more about IDE Support for Vue in the
30+
<a
31+
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
32+
target="_blank"
33+
>Vue Docs Scaling up Guide</a
34+
>.
35+
</p>
36+
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
37+
</template>
38+
39+
<style scoped>
40+
.read-the-docs {
41+
color: #888;
42+
}
43+
</style>

src/data-source.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const data = createLazyLoadData();
2+
3+
function createLazyLoadData() {
4+
let lazyLoadData = [];
5+
let customerid = ['VINET', 'TOMSP', 'HANAR', 'VICTE', 'SUPRD', 'HANAR', 'CHOPS', 'RICSU', 'WELLI', 'HILAA', 'ERNSH', 'CENTC',
6+
'OTTIK', 'QUEDE', 'RATTC', 'ERNSH', 'FOLKO', 'BLONP', 'WARTH', 'FRANK', 'GROSR', 'WHITC', 'WARTH', 'SPLIR', 'RATTC', 'QUICK', 'VINET',
7+
'MAGAA', 'TORTU', 'MORGK', 'BERGS', 'LEHMS', 'BERGS', 'ROMEY', 'ROMEY', 'LILAS', 'LEHMS', 'QUICK', 'QUICK', 'RICAR', 'REGGC', 'BSBEV',
8+
'COMMI', 'QUEDE', 'TRADH', 'TORTU', 'RATTC', 'VINET', 'LILAS', 'BLONP', 'HUNGO', 'RICAR', 'MAGAA', 'WANDK', 'SUPRD', 'GODOS', 'TORTU',
9+
'OLDWO', 'ROMEY', 'LONEP', 'ANATR', 'HUNGO', 'THEBI', 'DUMON', 'WANDK', 'QUICK', 'RATTC', 'ISLAT', 'RATTC', 'LONEP', 'ISLAT', 'TORTU',
10+
'WARTH', 'ISLAT', 'PERIC', 'KOENE', 'SAVEA', 'KOENE', 'BOLID', 'FOLKO', 'FURIB', 'SPLIR', 'LILAS', 'BONAP', 'MEREP', 'WARTH', 'VICTE',
11+
'HUNGO', 'PRINI', 'FRANK', 'OLDWO', 'MEREP', 'BONAP', 'SIMOB', 'FRANK', 'LEHMS', 'WHITC', 'QUICK', 'RATTC', 'FAMIA'];
12+
13+
let product = ['Chai', 'Chang', 'Aniseed Syrup', 'Chef Anton\'s Cajun Seasoning', 'Chef Anton\'s Gumbo Mix', 'Grandma\'s Boysenberry Spread',
14+
'Uncle Bob\'s Organic Dried Pears', 'Northwoods Cranberry Sauce', 'Mishi Kobe Niku', 'Ikura', 'Queso Cabrales', 'Queso Manchego La Pastora', 'Konbu',
15+
'Tofu', 'Genen Shouyu', 'Pavlova', 'Alice Mutton', 'Carnarvon Tigers', 'Teatime Chocolate Biscuits', 'Sir Rodney\'s Marmalade', 'Sir Rodney\'s Scones',
16+
'Gustaf\'s Knäckebröd', 'Tunnbröd', 'Guaraná Fantástica', 'NuNuCa Nuß-Nougat-Creme', 'Gumbär Gummibärchen', 'Schoggi Schokolade', 'Rössle Sauerkraut',
17+
'Thüringer Rostbratwurst', 'Nord-Ost Matjeshering', 'Gorgonzola Telino', 'Mascarpone Fabioli', 'Geitost', 'Sasquatch Ale', 'Steeleye Stout', 'Inlagd Sill',
18+
'Gravad lax', 'Côte de Blaye', 'Chartreuse verte', 'Boston Crab Meat', 'Jack\'s New England Clam Chowder', 'Singaporean Hokkien Fried Mee', 'Ipoh Coffee',
19+
'Gula Malacca', 'Rogede sild', 'Spegesild', 'Zaanse koeken', 'Chocolade', 'Maxilaku', 'Valkoinen suklaa', 'Manjimup Dried Apples', 'Filo Mix', 'Perth Pasties',
20+
'Tourtière', 'Pâté chinois', 'Gnocchi di nonna Alice', 'Ravioli Angelo', 'Escargots de Bourgogne', 'Raclette Courdavault', 'Camembert Pierrot', 'Sirop d\'érable',
21+
'Tarte au sucre', 'Vegie-spread', 'Wimmers gute Semmelknödel', 'Louisiana Fiery Hot Pepper Sauce', 'Louisiana Hot Spiced Okra', 'Laughing Lumberjack Lager', 'Scottish Longbreads',
22+
'Gudbrandsdalsost', 'Outback Lager', 'Flotemysost', 'Mozzarella di Giovanni', 'Röd Kaviar', 'Longlife Tofu', 'Rhönbräu Klosterbier', 'Lakkalikööri', 'Original Frankfurter grüne Soße'];
23+
24+
let customername = ['Maria', 'Ana Trujillo', 'Antonio Moreno', 'Thomas Hardy', 'Christina Berglund', 'Hanna Moos', 'Frédérique Citeaux', 'Martín Sommer', 'Laurence Lebihan', 'Elizabeth Lincoln',
25+
'Victoria Ashworth', 'Patricio Simpson', 'Francisco Chang', 'Yang Wang', 'Pedro Afonso', 'Elizabeth Brown', 'Sven Ottlieb', 'Janine Labrune', 'Ann Devon', 'Roland Mendel', 'Aria Cruz', 'Diego Roel',
26+
'Martine Rancé', 'Maria Larsson', 'Peter Franken', 'Carine Schmitt', 'Paolo Accorti', 'Lino Rodriguez', 'Eduardo Saavedra', 'José Pedro Freyre', 'André Fonseca', 'Howard Snyder', 'Manuel Pereira',
27+
'Mario Pontes', 'Carlos Hernández', 'Yoshi Latimer', 'Patricia McKenna', 'Helen Bennett', 'Philip Cramer', 'Daniel Tonini', 'Annette Roulet', 'Yoshi Tannamuri', 'John Steel', 'Renate Messner', 'Jaime Yorres',
28+
'Carlos González', 'Felipe Izquierdo', 'Fran Wilson', 'Giovanni Rovelli', 'Catherine Dewey', 'Jean Fresnière', 'Alexander Feuer', 'Simon Crowther', 'Yvonne Moncada', 'Rene Phillips', 'Henriette Pfalzheim',
29+
'Marie Bertrand', 'Guillermo Fernández', 'Georg Pipps', 'Isabel de Castro', 'Bernardo Batista', 'Lúcia Carvalho', 'Horst Kloss', 'Sergio Gutiérrez', 'Paula Wilson', 'Maurizio Moroni', 'Janete Limeira', 'Michael Holz',
30+
'Alejandra Camino', 'Jonas Bergulfsen', 'Jose Pavarotti', 'Hari Kumar', 'Jytte Petersen', 'Dominique Perrier', 'Art Braunschweiger', 'Pascale Cartrain', 'Liz Nixon', 'Liu Wong', 'Karin Josephs', 'Miguel Angel Paolino',
31+
'Anabela Domingues', 'Helvetius Nagy', 'Palle Ibsen', 'Mary Saveley', 'Paul Henriot', 'Rita Müller', 'Pirkko Koskitalo', 'Paula Parente', 'Karl Jablonski', 'Matti Karttunen', 'Zbyszek Piestrzeniewicz'];
32+
33+
let customeraddress = ['507 - 20th Ave. E.\r\nApt. 2A', '908 W. Capital Way', '722 Moss Bay Blvd.', '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.', 'Edgeham Hollow\r\nWinchester Way',
34+
'4726 - 11th Ave. N.E.', '7 Houndstooth Rd.', '59 rue de l\'Abbaye', 'Luisenstr. 48', '908 W. Capital Way', '722 Moss Bay Blvd.', '4110 Old Redmond Rd.', '14 Garrett Hill', 'Coventry House\r\nMiner Rd.', 'Edgeham Hollow\r\nWinchester Way',
35+
'7 Houndstooth Rd.', '2817 Milton Dr.', 'Kirchgasse 6', 'Sierras de Granada 9993', 'Mehrheimerstr. 369', 'Rua da Panificadora, 12', '2817 Milton Dr.', 'Mehrheimerstr. 369'];
36+
37+
let quantityperunit= ['10 boxes x 20 bags', '24 - 12 oz bottles', '12 - 550 ml bottles', '48 - 6 oz jars', '36 boxes', '12 - 8 oz jars', '12 - 1 lb pkgs.', '12 - 12 oz jars', '18 - 500 g pkgs.', '12 - 200 ml jars',
38+
'1 kg pkg.', '10 - 500 g pkgs.', '2 kg box', '40 - 100 g pkgs.', '24 - 250 ml bottles', '32 - 500 g boxes', '20 - 1 kg tins', '16 kg pkg.', '10 boxes x 12 pieces', '30 gift boxes', '24 pkgs. x 4 pieces', '24 - 500 g pkgs.', '12 - 250 g pkgs.',
39+
'12 - 355 ml cans', '20 - 450 g glasses', '100 - 250 g bags'];
40+
41+
let OrderID = 10248;
42+
for (let i = 0; i < 20000; i++) {
43+
lazyLoadData.push({
44+
'OrderID': OrderID + i,
45+
'CustomerID': customerid[Math.floor(Math.random() * customerid.length)],
46+
'CustomerName': customername[Math.floor(Math.random() * customername.length)],
47+
'CustomerAddress': customeraddress[Math.floor(Math.random() * customeraddress.length)],
48+
'ProductName': product[Math.floor(Math.random() * product.length)],
49+
'ProductID': i,
50+
'Quantity': quantityperunit[Math.floor(Math.random() * quantityperunit.length)]
51+
})
52+
}
53+
return lazyLoadData;
54+
}
55+
56+
export default data;

src/main.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createApp } from 'vue'
2+
import App from './App.vue'
3+
4+
createApp(App).mount('#app')

0 commit comments

Comments
 (0)