diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md index 6f0fe8c..2d170a1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # vue-data-grid-integration-with-custom-binding 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. +* Execute the following command to install the necessary dependencies: +`npm install` +* 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. +* Run the project using following command: +`npm run start` +Finally, the Syncfusion Vue Grid control will be rendered with custom binding. + \ No newline at end of file diff --git a/image/custom-binding-grid-action.gif b/image/custom-binding-grid-action.gif new file mode 100644 index 0000000..4a5745c Binary files /dev/null and b/image/custom-binding-grid-action.gif differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..8388c4b --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + +
+ + + +
+ Edit
+ components/HelloWorld.vue
to test HMR
+
+ Check out + create-vue, the official Vue + Vite starter +
++ Learn more about IDE Support for Vue in the + Vue Docs Scaling up Guide. +
+Click on the Vite and Vue logos to learn more
+ + + diff --git a/src/data-source.js b/src/data-source.js new file mode 100644 index 0000000..d452385 --- /dev/null +++ b/src/data-source.js @@ -0,0 +1,56 @@ +const data = createLazyLoadData(); + +function createLazyLoadData() { + let lazyLoadData = []; + let customerid = ['VINET', 'TOMSP', 'HANAR', 'VICTE', 'SUPRD', 'HANAR', 'CHOPS', 'RICSU', 'WELLI', 'HILAA', 'ERNSH', 'CENTC', + 'OTTIK', 'QUEDE', 'RATTC', 'ERNSH', 'FOLKO', 'BLONP', 'WARTH', 'FRANK', 'GROSR', 'WHITC', 'WARTH', 'SPLIR', 'RATTC', 'QUICK', 'VINET', + 'MAGAA', 'TORTU', 'MORGK', 'BERGS', 'LEHMS', 'BERGS', 'ROMEY', 'ROMEY', 'LILAS', 'LEHMS', 'QUICK', 'QUICK', 'RICAR', 'REGGC', 'BSBEV', + 'COMMI', 'QUEDE', 'TRADH', 'TORTU', 'RATTC', 'VINET', 'LILAS', 'BLONP', 'HUNGO', 'RICAR', 'MAGAA', 'WANDK', 'SUPRD', 'GODOS', 'TORTU', + 'OLDWO', 'ROMEY', 'LONEP', 'ANATR', 'HUNGO', 'THEBI', 'DUMON', 'WANDK', 'QUICK', 'RATTC', 'ISLAT', 'RATTC', 'LONEP', 'ISLAT', 'TORTU', + 'WARTH', 'ISLAT', 'PERIC', 'KOENE', 'SAVEA', 'KOENE', 'BOLID', 'FOLKO', 'FURIB', 'SPLIR', 'LILAS', 'BONAP', 'MEREP', 'WARTH', 'VICTE', + 'HUNGO', 'PRINI', 'FRANK', 'OLDWO', 'MEREP', 'BONAP', 'SIMOB', 'FRANK', 'LEHMS', 'WHITC', 'QUICK', 'RATTC', 'FAMIA']; + + let product = ['Chai', 'Chang', 'Aniseed Syrup', 'Chef Anton\'s Cajun Seasoning', 'Chef Anton\'s Gumbo Mix', 'Grandma\'s Boysenberry Spread', + 'Uncle Bob\'s Organic Dried Pears', 'Northwoods Cranberry Sauce', 'Mishi Kobe Niku', 'Ikura', 'Queso Cabrales', 'Queso Manchego La Pastora', 'Konbu', + 'Tofu', 'Genen Shouyu', 'Pavlova', 'Alice Mutton', 'Carnarvon Tigers', 'Teatime Chocolate Biscuits', 'Sir Rodney\'s Marmalade', 'Sir Rodney\'s Scones', + 'Gustaf\'s Knäckebröd', 'Tunnbröd', 'Guaraná Fantástica', 'NuNuCa Nuß-Nougat-Creme', 'Gumbär Gummibärchen', 'Schoggi Schokolade', 'Rössle Sauerkraut', + 'Thüringer Rostbratwurst', 'Nord-Ost Matjeshering', 'Gorgonzola Telino', 'Mascarpone Fabioli', 'Geitost', 'Sasquatch Ale', 'Steeleye Stout', 'Inlagd Sill', + 'Gravad lax', 'Côte de Blaye', 'Chartreuse verte', 'Boston Crab Meat', 'Jack\'s New England Clam Chowder', 'Singaporean Hokkien Fried Mee', 'Ipoh Coffee', + 'Gula Malacca', 'Rogede sild', 'Spegesild', 'Zaanse koeken', 'Chocolade', 'Maxilaku', 'Valkoinen suklaa', 'Manjimup Dried Apples', 'Filo Mix', 'Perth Pasties', + 'Tourtière', 'Pâté chinois', 'Gnocchi di nonna Alice', 'Ravioli Angelo', 'Escargots de Bourgogne', 'Raclette Courdavault', 'Camembert Pierrot', 'Sirop d\'érable', + 'Tarte au sucre', 'Vegie-spread', 'Wimmers gute Semmelknödel', 'Louisiana Fiery Hot Pepper Sauce', 'Louisiana Hot Spiced Okra', 'Laughing Lumberjack Lager', 'Scottish Longbreads', + 'Gudbrandsdalsost', 'Outback Lager', 'Flotemysost', 'Mozzarella di Giovanni', 'Röd Kaviar', 'Longlife Tofu', 'Rhönbräu Klosterbier', 'Lakkalikööri', 'Original Frankfurter grüne Soße']; + + let customername = ['Maria', 'Ana Trujillo', 'Antonio Moreno', 'Thomas Hardy', 'Christina Berglund', 'Hanna Moos', 'Frédérique Citeaux', 'Martín Sommer', 'Laurence Lebihan', 'Elizabeth Lincoln', + 'Victoria Ashworth', 'Patricio Simpson', 'Francisco Chang', 'Yang Wang', 'Pedro Afonso', 'Elizabeth Brown', 'Sven Ottlieb', 'Janine Labrune', 'Ann Devon', 'Roland Mendel', 'Aria Cruz', 'Diego Roel', + 'Martine Rancé', 'Maria Larsson', 'Peter Franken', 'Carine Schmitt', 'Paolo Accorti', 'Lino Rodriguez', 'Eduardo Saavedra', 'José Pedro Freyre', 'André Fonseca', 'Howard Snyder', 'Manuel Pereira', + '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', + 'Carlos González', 'Felipe Izquierdo', 'Fran Wilson', 'Giovanni Rovelli', 'Catherine Dewey', 'Jean Fresnière', 'Alexander Feuer', 'Simon Crowther', 'Yvonne Moncada', 'Rene Phillips', 'Henriette Pfalzheim', + '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', + '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', + 'Anabela Domingues', 'Helvetius Nagy', 'Palle Ibsen', 'Mary Saveley', 'Paul Henriot', 'Rita Müller', 'Pirkko Koskitalo', 'Paula Parente', 'Karl Jablonski', 'Matti Karttunen', 'Zbyszek Piestrzeniewicz']; + + 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', + '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', + '7 Houndstooth Rd.', '2817 Milton Dr.', 'Kirchgasse 6', 'Sierras de Granada 9993', 'Mehrheimerstr. 369', 'Rua da Panificadora, 12', '2817 Milton Dr.', 'Mehrheimerstr. 369']; + + 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', + '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.', + '12 - 355 ml cans', '20 - 450 g glasses', '100 - 250 g bags']; + + let OrderID = 10248; + for (let i = 0; i < 20000; i++) { + lazyLoadData.push({ + 'OrderID': OrderID + i, + 'CustomerID': customerid[Math.floor(Math.random() * customerid.length)], + 'CustomerName': customername[Math.floor(Math.random() * customername.length)], + 'CustomerAddress': customeraddress[Math.floor(Math.random() * customeraddress.length)], + 'ProductName': product[Math.floor(Math.random() * product.length)], + 'ProductID': i, + 'Quantity': quantityperunit[Math.floor(Math.random() * quantityperunit.length)] + }) + } + return lazyLoadData; +} + +export default data; \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..01433bc --- /dev/null +++ b/src/main.js @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/src/orderService.ts b/src/orderService.ts new file mode 100644 index 0000000..bd386d3 --- /dev/null +++ b/src/orderService.ts @@ -0,0 +1,184 @@ +import { DataManager, Query } from "@syncfusion/ej2-data"; + +export class OrderService { +} +export {}; +const baseUrl = "http://localhost:xxxx/orders"; // Here xxxx denotes the port number. +let gridData:DataManager; +// Apply filtering +const applyFiltering = (query, filter)=> { + // Check if filter columns are specified + if (filter.columns && filter.columns.length) { + // Apply filtering for each specified column + for (let i = 0; i < filter.columns.length; i++) { + const field = filter.columns[i].field; + const operator = filter.columns[i].operator; + const value = filter.columns[i].value; + query.where(field, operator, value); + } + } + else { + // Apply filtering based on direct filter conditions + for (let i = 0; i < filter.length; i++) { + const { fn, e } = filter[i]; + if (fn === 'onWhere') { + query.where(e as string); + } + } + } + } +// Apply searching +const applySearching = (query, search)=> { + // Check if a search operation is requested + if (search && search.length > 0) { + // Extract the search key and fields from the search array + const { fields, key } = search[0]; + // perform search operation using the field and key on the query + query.search(key, fields); + } +} +// Apply sorting +const applySorting = (query, sorted) =>{ + // Check if sorting data is available + if (sorted && sorted.length > 0) { + // Iterate through each sorting info + sorted.forEach(sort => { + // Get the sort field name either by name or field + const sortField = sort.name || sort.field; + // Perform sort operation using the query based on the field name and direction + query.sortBy(sortField, sort.direction); + }); + } +} +// Apply grouping +const applyGrouping = (query, group) =>{ + // Check if sorting data is available + if (group.length > 0) { + // Iterate through each group info + group.forEach((column: string) => { + // perform group operation using the column on the query + query.group(column); + }); + } +} +// Apply lazy load grouping +const applyLazyLoad = (query, payload) => { + // Configure lazy loading for the main data + if (payload.isLazyLoad) { + query.lazyLoad.push({ key: 'isLazyLoad', value: true }); + // If on-demand group loading is enabled, configure lazy loading for grouped data + if (payload.onDemandGroupInfo) { + query.lazyLoad.push({ + key: 'onDemandGroupInfo', + value: payload.action.lazyLoadQuery, + }); + } + } +} +// Apply paging +const applyPaging = (query, state)=> { + // Check if both 'take' and 'skip' values are available + if (state.take && state.skip) { + // Calculate pageSkip and pageTake values to get pageIndex and pageSize + const pageSkip = state.skip / state.take + 1; + const pageTake = state.take; + query.page(pageSkip, pageTake); + } + // If if only 'take' is available and 'skip' is 0, apply paging for the first page. + else if (state.skip === 0 && state.take) { + query.page(1, state.take); + } +} +export function getOrders(state, action) { + const query = new Query(); + // filter + if (state.where) { + applyFiltering(query, action.queries); + } + // search + if (state.search) { + applySearching(query, state.search); + }; + // sort + if (state.sorted) { + state.sorted.length ? applySorting(query, state.sorted) : + // initial sorting + state.sorted.columns.length ? applySorting(query, state.sorted.columns) : null + } + // grouping + if (state.group) { + state.group.length ? applyGrouping(query, state.group) : + // initial grouping + state.group.columns.length ? applyGrouping(query, state.group.columns) : null + } + // lazy load grouping + if (state.group) { + if (state.isLazyLoad) { + applyLazyLoad(query, state) + } + if (state.group.enableLazyLoading) { + query.lazyLoad.push({ key: 'isLazyLoad', value: true }) + } + } + // page + applyPaging(query, state) + query.isCountRequired = true + + // Request the data from server using fetch + return fetch(baseUrl) + .then(res => res.json()) + .then(data => { + // Create a DataManager instance with your fetched data + gridData = new DataManager(data.result); + + // Execute local data operations using the provided query + const result = gridData.executeLocal(query); + + // Return the result along with the count of total records + return { + result: result, // Result of the data + count: (result as any).count // Total record count based on fetched data length + }; + }); +} + +// add +export function addRecord(order) { + return fetch(baseUrl , { + method: "post", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + value: order + }) + }) + .then((data) => { + return data; + }); +} + +// update +export function updateRecord(order) { + return fetch(`${baseUrl}/${order.OrderID}`, { + method: "put", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + value: order + }) + }) + .then(data => { + return data; + }); +} + +// delete +export function deleteRecord(primaryKey) { + return fetch(`${baseUrl}/${primaryKey}`, { + method: "delete", + body: JSON.stringify({ + value: primaryKey + }) + }) + .then(data => { + return data; + }); +} \ No newline at end of file diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..05c1740 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], +})