Smart.Grid Data Binding with Vue

Smart.Grid Data Binding with Vue

We don't always get the data for a grid locally, sometimes we may have different data sources. This requires a flexible grid component, so our Smart.Grid fulfills this requirement as it supports different ways of binding the data. In this topic, we will cover four ways of doing so:
  • - locally
  • - from a file
  • - server-side with DataAdapter's features
  • - with ajax

Introduction to Data Adapter

Smart.DataAdapter is our utility for easily binding both local and remote data. This tool makes the binding an easy task, so we will use it for every binding approach. More information here: Smart.DataAdapter

Setup Vue 3 project

You can follow this tutorial or just follow this steps:

  1. Create a folder called smart-vue-app
  2. Open the terminal and type
    npm init vue@latest

    Use the following settings

    Project name: smart-vue-app
    Add TypeScript: No
    Add JSX Support: No
    Add Vue Router for Single Page Application development: Yes
    Add Pinia for state management: No
    Add Vitest for Unit Testing: No
    Add an End-to-End Testing Solution: No
    Add ESLint for code quality: No
  3. Navigate to the folder
    cd smart-vue-app
  4. Install the dependencies
    npm install
  5. Install our package:
    npm install smart-webcomponents
  6. Run the application
    npm run dev
  7. Clear the unnecessary items:

    Open main.js and remove

    import './assets/main.css'

    Remove the src/assets folder

    Remove the src/components folder

    Remove the views from src/views

    Open App.vue and paste this:

    <script setup>
    import { RouterLink, RouterView } from "vue-router";
    import 'smart-webcomponents/source/styles/smart.default.css';
    </script>
    
    <template>
      <header>
        <nav id="nav">
          <RouterLink to="/locally" class="text-red">Locally</RouterLink>
          <RouterLink to="/from-file" class="text-red">From file</RouterLink>
          <RouterLink to="/server-side" class="text-red">Server-side with DataAdapter's features</RouterLink>
          <RouterLink to="/ajax" class="text-red">With AJAX</RouterLink>
        </nav>
      </header>
      <RouterView />
    </template>
    
    <style>
    body {
      margin: 0 20px;
      font-family: var(--smart-font-family);
    }
    
    .text-red {
      color: #fc3752;
    }
    
    #nav {
      padding: 1rem 3rem;
      display: flex;
      gap: 20px;
    }
    
    nav a {
      text-decoration: none;
    }
    
    @media screen and (max-width: 580px) {
      #nav {
        flex-direction: column;
      }
    }
    </style>

    Open src/router/index.js and paste the following:

    import { createRouter, createWebHistory } from 'vue-router'
    
    const router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes: []
    })
    
    export default router

  8. Open vite.config.js to register smart-elements as custom elements with this option:

    export default defineConfig({
      plugins: [vue({
        template: {
          compilerOptions: {
            isCustomElement: tag => tag.startsWith('smart-')
          }
        }
      })],
      resolve: {
        alias: {
          '@': fileURLToPath(new URL('./src', import.meta.url))
        }
      }
    })

Binding data locally

We have a router so we should define our default route to be "/locally". To do that we should have a corresponding component. In src/views create a file called: LocallyView.vue and paste this:

<script setup></script>
<template></template>

To register this route, open src/router/index.js and inside the routes array, add a route objects, one to redirect from "/" to "/locally" and the "/locally" route:

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    { path: '/', redirect: '/locally' },
    { path: '/locally', component: LocallyView }
  ]
})

We are ready to create our Smart.Grid with local data. The data will be generated by a function in our LocallyView.vue component and will be passed to the Smart.Grid via the Smart.DataAdapter

The data will be stored in a Vue ref(). Vue refs are part of the Vue's Reactivity API. See more here.

Inside the script tag we should have the function for generating the data, data ref and inside the onMounted hook we will initialize our Smart.Grid.

<script setup>
import { ref, onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";

function generateData(records) {
  let data = [];

  let firstNames = [
    "Andrew",
    "Nancy",
    "Shelley",
    "Regina",
    "Yoshi",
    "Antoni",
    "Mayumi",
    "Ian",
    "Peter",
    "Lars",
  ];

  let lastNames = [
    "Fuller",
    "Davolio",
    "Burke",
    "Murphy",
    "Nagase",
    "Saavedra",
    "Ohno",
    "Devling",
    "Wilson",
    "Peterson",
  ];

  let productNames = [
    "Black Tea",
    "Green Tea",
    "Caffe Espresso",
    "Doubleshot Espresso",
    "Caffe Latte",
    "White Chocolate Mocha",
    "Cramel Latte",
  ];

  let priceValues = [
    "2.25",
    "1.5",
    "3.3",
    "4.5",
    "3.6",
    "5.0",
    "1.75"
  ];

  for (let i = 0; i < records; i++) {
    let row = {};
    let productindex = Math.floor(Math.random() * productNames.length);
    let price = parseFloat(priceValues[productindex]);
    let quantity = 1 + Math.round(Math.random() * 10);
    row["firstName"] = firstNames[Math.floor(Math.random() * firstNames.length)];
    row["lastName"] = lastNames[Math.floor(Math.random() * lastNames.length)];
    row["productName"] = productNames[productindex];
    row["price"] = price;
    row["quantity"] = quantity;
    row["total"] = price * quantity;
    data[i] = row;
  }

  return data;
}

const data = ref(generateData(50));

onMounted(() => {
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          dataSource: new window.Smart.DataAdapter({
            dataSource: data.value,
            dataFields: [
              "firstName: string",
              "lastName: string",
              "productName: string",
              "quantity: number",
              "price: number",
              "total: number",
            ],
          }),
          columns: [
            {
              label: "First Name",
              dataField: "firstName",
            },
            {
              label: "Last Name",
              dataField: "lastName",
            },
            {
              label: "Product",
              dataField: "productName",
            },
            {
              label: "Quantity",
              dataField: "quantity",
            },
            {
              label: "Unit Price",
              dataField: "price",
              cellsFormat: "c2",
            },
          ],
        };
      }
    }
  );
});
</script>
<template>
  <p>Smart.Grid bound to local data generated by a simple function</p>
  <smart-grid id="grid"></smart-grid>
</template>
<style scoped>
  smart-grid {
    width: 100%;
  }
</style>

Now we will add a Smart.Button from which we will update dynamically the data in the Smart.Grid

We will additionally need a template ref for the grid in the template and a watcher to watch the data ref()

More information about watchers may be seen here

<script setup>
import { ref, watch, onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";
import "smart-webcomponents/source/modules/smart.button.js";

function generateData(records) {
  let data = [];

  let firstNames = [
    "Andrew",
    "Nancy",
    "Shelley",
    "Regina",
    "Yoshi",
    "Antoni",
    "Mayumi",
    "Ian",
    "Peter",
    "Lars",
  ];

  let lastNames = [
    "Fuller",
    "Davolio",
    "Burke",
    "Murphy",
    "Nagase",
    "Saavedra",
    "Ohno",
    "Devling",
    "Wilson",
    "Peterson",
  ];

  let productNames = [
    "Black Tea",
    "Green Tea",
    "Caffe Espresso",
    "Doubleshot Espresso",
    "Caffe Latte",
    "White Chocolate Mocha",
    "Cramel Latte",
  ];

  let priceValues = ["2.25", "1.5", "3.3", "4.5", "3.6", "5.0", "1.75"];

  for (let i = 0; i < records; i++) {
    let row = {};
    let productindex = Math.floor(Math.random() * productNames.length);
    let price = parseFloat(priceValues[productindex]);
    let quantity = 1 + Math.round(Math.random() * 10);
    row["firstName"] = firstNames[Math.floor(Math.random() * firstNames.length)];
    row["lastName"] = lastNames[Math.floor(Math.random() * lastNames.length)];
    row["productName"] = productNames[productindex];
    row["price"] = price;
    row["quantity"] = quantity;
    row["total"] = price * quantity;
    data[i] = row;
  }

  return data;
}

function updateData() {
  data.value = generateData(50);
}

const gridRef = ref(null);
const data = ref(generateData(50));

watch(data, (newData) => {
  gridRef.value.dataSource = new window.Smart.DataAdapter({
    dataSource: newData,
    dataFields: [
      "firstName: string",
      "lastName: string",
      "productName: string",
      "quantity: number",
      "price: number",
      "total: number",
    ],
  });
});

onMounted(() => {
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          dataSource: new window.Smart.DataAdapter({
            dataSource: data.value,
            dataFields: [
              "firstName: string",
              "lastName: string",
              "productName: string",
              "quantity: number",
              "price: number",
              "total: number",
            ],
          }),
          columns: [
            {
              label: "First Name",
              dataField: "firstName",
            },
            {
              label: "Last Name",
              dataField: "lastName",
            },
            {
              label: "Product",
              dataField: "productName",
            },
            {
              label: "Quantity",
              dataField: "quantity",
            },
            {
              label: "Unit Price",
              dataField: "price",
              cellsFormat: "c2",
            },
          ],
        };
      }
    }
  );
});
</script>
<template>
  <p>Smart.Grid bound to local data generated by a simple function</p>
  <p>
    <smart-button v-on:click="updateData">Update Grid Data</smart-button>
  </p>
  <smart-grid ref="gridRef" id="grid"></smart-grid>
</template>
<style scoped>
smart-grid {
  width: 100%;
}
</style>

In this section, we managed to bind local data generated by a function and after a button's click generate new data and update the Smart.Grid.

All of this thanks to the Vue's API.

We have used:

  • - ref() to save the data between re-renders
  • - onMount lifecycle hook to initialize the grid
  • - watcher to track the data ref() change and upon a change, update the grid with the new data

Binding data from file

The next option for the source of the data is a file. In this section, we will create a Vue component with Smart.Grid that will get the data from a JSON file.

The first step is to create a new Vue component in src/views called DataFileView.vue and again paste the following:

<script setup></script>
<template></template>

Now to access that view, we do need a route for it, so open src/router/index.js and add a route object:

{ path: '/from-file', component: DataFileView }

NOTE that the DataFileView should be imported!

import DataFileView from '../views/DataFileView.vue'

We need to get the data from a file, so we need to create it. There are two options for binding data from a file. The first one is importing the file in the component and using the array like local data or hosting the file and accessing it via its path from the Smart.DataAdapter

For the first way, we need a file and it is going to be saved in src/assets folder so we should create it.

After creating this folder, create a file called data.json and fill it with this data:

[{
 "id": "1",
 "name": "Hot Chocolate",
 "type": "Chocolate Beverage",
 "calories": "370",
 "totalfat": "16g",
 "protein": "14g"
 }, {
 "id": 2,
 "name": "Peppermint Hot Chocolate",
 "type": "Chocolate Beverage",
 "calories": "440",
 "totalfat": "16g",
 "protein": "13g"
}, {
 "id": "3",
 "name": "Salted Caramel Hot Chocolate",
 "type": "Chocolate Beverage",
 "calories": "450",
 "totalfat": "16g",
 "protein": "13g"
}, {
 "id": "4",
 "name": "White Hot Chocolate",
 "type": "Chocolate Beverage",
 "calories": "420",
 "totalfat": "16g",
 "protein": "12g"
}, {
 "id": "5",
 "name": "Caffe Americano",
 "type": "Espresso Beverage",
 "calories": "15",
 "totalfat": "0g",
 "protein": "1g"
}, {
 "id": "6",
 "name": "Caffe Latte",
 "type": "Espresso Beverage",
 "calories": "190",
 "totalfat": "7g",
 "protein": "12g"
}, {
 "id": "7",
 "name": "Caffe Mocha",
 "type": "Espresso Beverage",
 "calories": "330",
 "totalfat": "15g",
 "protein": "13g"
}, {
 "id": "8",
 "name": "Cappuccino",
 "type": "Espresso Beverage",
 "calories": "120",
 "totalfat": "4g",
 "protein": "8g"
}, {
 "id": "9",
 "name": "Caramel Brulee Latte",
 "type": "Espresso Beverage",
 "calories": "420",
 "totalfat": "9g",
 "protein": "8g"
}]

Now it is time to create the Smart.Grid and the process is similar to the previous section.

In the template, we have a <smart-grid> tag, which is styled with width: 100% and height: auto

The initialization of the Smart.Grid is again in the onMounted hook.

We need to import the data as a variable so we can do that this way:

import data from '../assets/data.json';

After importing the data, you can set it similarly to the previous section

The result is the following:

<script setup>
import { onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";

import data from '../assets/data.json';

onMounted(() => {
  
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          appearance: {
            alternationStart: 0,
            alternationCount: 2,
          },
          selection: {
            enabled: true,
            checkBoxes: {
              enabled: true,
            },
          },
          filtering: {
            enabled: true,
          },
          sorting: {
            enabled: true,
          },
          dataSource: new window.Smart.DataAdapter({
            dataSource: data,
            dataFields: [
              {
                name: "name",
                dataType: "string",
              },
              {
                name: "type",
                dataType: "string",
              },
              {
                name: "calories",
                dataType: "int",
              },
              {
                name: "totalfat",
                dataType: "string",
              },
              {
                name: "protein",
                dataType: "string",
              },
            ],
          }),
          columns: [
            {
              label: "Name",
              dataField: "name"
            },
            {
              label: "Beverage Type",
              dataField: "type"
            },
            {
              label: "Calories",
              dataField: "calories"
            },
            {
              label: "Total Fat",
              dataField: "totalfat"
            },
            {
              label: "Protein",
              dataField: "protein"
            },
          ],
        };
      }
    }
  );
});
</script>
<template>
  <smart-grid id="grid"></smart-grid>
</template>
<style scoped>
smart-grid {
  width: 100%;
  height: auto;
}
</style>

Another option is to set the dataSource of the Smart.DataAdapter to the path of the file which should be hosted. To do that simply create a folder assets in the public folder and copy the data.json in it. You should have the following hierarchy public/assets/data.json.

In the Smart.DataAdapter settings change the dataSource property to 'assets/data.json'

The code should look like this:

The result is the following:

<script setup>
import { onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";

onMounted(() => {
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          appearance: {
            alternationStart: 0,
            alternationCount: 2,
          },
          selection: {
            enabled: true,
            checkBoxes: {
              enabled: true,
            },
          },
          filtering: {
            enabled: true,
          },
          sorting: {
            enabled: true,
          },
          dataSource: new window.Smart.DataAdapter({
            dataSource: 'assets/data.json',
            dataFields: [
              {
                name: "name",
                dataType: "string",
              },
              {
                name: "type",
                dataType: "string",
              },
              {
                name: "calories",
                dataType: "int",
              },
              {
                name: "totalfat",
                dataType: "string",
              },
              {
                name: "protein",
                dataType: "string",
              },
            ],
          }),
          columns: [
            {
              label: "Name",
              dataField: "name"
            },
            {
              label: "Beverage Type",
              dataField: "type"
            },
            {
              label: "Calories",
              dataField: "calories"
            },
            {
              label: "Total Fat",
              dataField: "totalfat"
            },
            {
              label: "Protein",
              dataField: "protein"
            },
          ],
        };
      }
    }
  );
});
</script>
<template>
  <smart-grid id="grid"></smart-grid>
</template>
<style scoped>
smart-grid {
  width: 100%;
  height: auto;
}
</style>

Binding data to a server

Again we should create a component ServerSideView.vue in src/views and then register it as a route. The route object will be the following:

{ path: '/server-side', component: ServerSideView }

For this section, we do need a server, so download this server. To run the server, just extract the project from the archive, open a terminal and run

npm install
then run
npm start

Grid Actions

In this subsection, we will cover the grid actions. These actions are Sorting, Paging and Filtering. They will be handled by the 'virtualDataSource' property of our Smart.DataAdapter. The property accepts resultCallbackFunction and details. The resultCallbackFunction must pass an object with the data:

{ dataSource: array }

The details argument contains information about the paging, sorting, filtering and the currently performed action.

Our ServerSideView.vue with an empty Smart.Grid looks like this:

<script setup>
import { ref, onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";

const gridRef = ref(null);

onMounted(() => {
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          sorting: {
            enabled: true,
          },
          filtering: {
            enabled: true,
          },
          paging: {
            enabled: true,
            pageSize: 10,
            pageIndex: 0,
          },
          pager: {
            position: "far",
            visible: true,
          },
          dataSource: new window.Smart.DataAdapter({
            virtualDataSource: function (resultCallbackFunction, details) {

            },
            dataFields: [
              "id: number",
              "firstName: string",
              "lastName: string",
              "productName: string",
              "quantity: number",
              "price: number",
              "total: number",
            ],
          }),
          columns: [
            "id",
            {
              label: "First Name",
              dataField: "firstName",
            },
            {
              label: "Last Name",
              dataField: "lastName",
            },
            {
              label: "Product",
              dataField: "productName",
            },
            {
              label: "Quantity",
              dataField: "quantity",
            },
            {
              label: "Unit Price",
              dataField: "price",
              cellsFormat: "c2",
            },
            {
              label: "Total",
              dataField: "total",
              cellsFormat: "c2",
            },
          ],
        };
      }
    }
  );
});
</script>
<template>
<p>
  Smart.Grid bound to a server. The server will support paging, sorting, filtering and
  CRUD operations
</p>
<smart-grid ref="gridRef" id="grid"></smart-grid>
</template>
<style scoped>
smart-grid {
width: 100%;
}
</style>

Our next step is to fetch the data from our server. For this task we will create a method callded getData to which we will pass the details. This method will return a Promise that resolves to the data. For getting detailed information about the state of the grid, we will use the getState method from the grid. In our getData method, we are going to modify the filtering and sorting properties of the details object. The last step is to fetch the data from the server.

So far we achieved this:

<script setup>
import { ref, onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";

const apiUrl = "http://localhost:3005";

const gridRef = ref(null);

const getData = (details) => {

  const state = gridRef.value.getState();

  const filtering = {};
  Object.keys(state.filter)
    .forEach((f) => {
      filtering[f] = {
        filters: state.filter[f].filters,
        logicalOperators: state.filter[f].logicalOperators,
      }
  })
  details.filtering = filtering;

  const sorting = {};
  Object.keys(details.sorting)
  .forEach((s) => {
    sorting[s] = details.sorting[s]
  })
  details.sorting = sorting;

  return fetch(`${apiUrl}/products`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(details),
  })
    .then((res) => res.json())
    .catch(() => ({ data: [], length: 0 }));
};

onMounted(() => {
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          sorting: {
            enabled: true,
            mode: 'many'
          },
          filtering: {
            enabled: true,
          },
          paging: {
            enabled: true,
            pageSize: 10,
            pageIndex: 0,
          },
          pager: {
            position: "far",
            visible: true,
          },
          dataSource: new window.Smart.DataAdapter({
            virtualDataSource: (resultCallbackFunction, details) => {

              getData(details)
                .then((responseData) => {
                  resultCallbackFunction({
                    dataSource: responseData.data,
                    virtualDataSourceLength: responseData.length,
                  })
                })

            },
            dataFields: [
              "id: number",
              "firstName: string",
              "lastName: string",
              "productName: string",
              "quantity: number",
              "price: number",
              "total: number",
            ],
          }),
          columns: [
            "id",
            {
              label: "First Name",
              dataField: "firstName",
            },
            {
              label: "Last Name",
              dataField: "lastName",
            },
            {
              label: "Product",
              dataField: "productName",
            },
            {
              label: "Quantity",
              dataField: "quantity",
            },
            {
              label: "Unit Price",
              dataField: "price",
              cellsFormat: "c2",
            },
            {
              label: "Total",
              dataField: "total",
              cellsFormat: "c2",
            },
          ],
        };
      }
    }
  );
});
</script>
<template>
  <p>
    Smart.Grid bound to a server. The server will support paging, sorting, filtering and
    CRUD operations
  </p>
  <smart-grid ref="gridRef" id="grid"></smart-grid>
</template>
<style scoped>
smart-grid {
  width: 100%;
}
</style>

You can see that we are sending a POST request. We are using a POST method because the filtering and sorting are complex and we do need to send objects. When we have complex data, it is easier to pass a JSON body, instead of using query params.

Grid Server-Side CRUD operations

It is time to bind the CRUD operations on the client with the server. We should check the action: details.action and based on it we will have different handlers that communicate with the server. Take a look at ServerSideView.vue:

<script setup>
import { ref, onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";

const apiUrl = "http://localhost:3005";

const gridRef = ref(null);

const getData = (details) => {
  normalizeDetails(details);

  return fetch(`${apiUrl}/products`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(details),
  })
    .then((res) => res.json())
    .catch(() => ({ data: [], length: 0 }));
};

const addNewRow = (details) => {
  normalizeDetails(details);

  return fetchAPI("add", "POST", details)
};

const editRow = (details) => {
  normalizeDetails(details);

  return fetchAPI("edit", "PUT", details);
};

const deleteRow = (details) => {
  
  normalizeDetails(details);

  return fetchAPI("delete", "DELETE", details);
};

const fetchAPI = (path, method, details) =>
  fetch(`${apiUrl}/products/${path}`, {
    method: method,
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(details),
  })
    .then((res) => res.json())
    .catch(() => ({ data: [], length: 0 }));

function normalizeDetails(details) {
  const state = gridRef.value.getState();

  const filtering = {};
  Object.keys(state.filter).forEach((f) => {
    filtering[f] = {
      filters: state.filter[f].filters,
      logicalOperators: state.filter[f].logicalOperators,
    };
  });
  details.filtering = filtering;

  const sorting = {};
  Object.keys(details.sorting).forEach((s) => {
    sorting[s] = details.sorting[s];
  });
  details.sorting = sorting;
}

onMounted(() => {
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          editing: {
            enabled: true,
            action: "none",
            mode: "row",
            addNewRow: {
              visible: true,
              position: "far",
              autoEdit: true,
            },
            commandColumn: {
              visible: true,
              width: 100,
            },
          },
          selection: {
            enabled: true,
            allowCellSelection: true,
          },
          dataSource: new window.Smart.DataAdapter({
            virtualDataSource: (resultCallbackFunction, details) => {
              
              switch (details.action) {
                case "add":
                  addNewRow(details).then((responseData) => {
                    
                    resultCallbackFunction({
                      dataSource: responseData.data,
                      virtualDataSourceLength: responseData.length,
                      lastId: responseData.length
                    });

                    gridRef.value.lastPage();
                  });
                  break;
                case "update":
                  editRow(details).then((responseData) => {
                    resultCallbackFunction({
                      dataSource: responseData.data,
                      virtualDataSourceLength: responseData.length,
                    });
                  });
                  break;
                case "remove":
                  deleteRow(details).then((responseData) => {
                    resultCallbackFunction({
                      dataSource: responseData.data,
                      virtualDataSourceLength: responseData.length,
                    });
                  });
                  break;
                default:
                  getData(details).then((responseData) => {
                    resultCallbackFunction({
                      dataSource: responseData.data,
                      virtualDataSourceLength: responseData.length,
                    });
                  });
                  break;
              }
            },
            id: 'id',
            dataFields: [
              "id: number",
              "firstName: string",
              "lastName: string",
              "productName: string",
              "quantity: number",
              "price: number",
              "total: number",
            ],
          }),
          columns: [
            { label: "Id", dataField: "id", allowEdit: false },
            {
              label: "First Name",
              dataField: "firstName",
            },
            {
              label: "Last Name",
              dataField: "lastName",
            },
            {
              label: "Product",
              dataField: "productName",
            },
            {
              label: "Quantity",
              dataField: "quantity",
            },
            {
              label: "Unit Price",
              dataField: "price",
              cellsFormat: "c2",
            },
            {
              label: "Total",
              dataField: "total",
              cellsFormat: "c2",
            },
          ],
        };
      }
    }
  );
});
</script>
<template>
  <p>
    Smart.Grid bound to a server. The server will support paging, sorting, filtering and
    CRUD operations
  </p>
  <smart-grid ref="gridRef" id="grid"></smart-grid>
</template>
<style scoped>
smart-grid {
  width: 100%;
}
</style>

Grid binding with AJAX

Here, we will bind data from an AJAX request from our server.

We will repeat the steps for creating a component and registering the route.

In the onMount hook, we will fetch the data and assign it to our dataSource

The result is the following:

<script setup>
import { ref, onMounted } from "vue";

import "smart-webcomponents/source/modules/smart.grid.js";

const apiUrl = "http://localhost:3005";

const gridRef = ref(null);

onMounted(() => {
  window.Smart(
    "#grid",
    class {
      get properties() {
        return {
          editing: {
            enabled: true,
            action: "none",
            mode: "row",
            addNewRow: {
              visible: true,
              position: "far",
              autoEdit: true,
            },
            commandColumn: {
              visible: true,
              width: 100,
            },
          },
          selection: {
            enabled: true,
            allowCellSelection: true,
          },
          columns: [
            {
              label: "First Name",
              dataField: "firstName",
            },
            {
              label: "Last Name",
              dataField: "lastName",
            },
            {
              label: "Product",
              dataField: "productName",
            },
            {
              label: "Quantity",
              dataField: "quantity",
            },
            {
              label: "Unit Price",
              dataField: "price",
              cellsFormat: "c2",
            },
            {
              label: "Total",
              dataField: "total",
              cellsFormat: "c2",
            },
          ],
        };
      }
    }
  );

  fetch(`${apiUrl}/products`)
    .then((res) => res.json())
    .then((data) => {
      gridRef.value.dataSource = new window.Smart.DataAdapter({
        dataSource: data,
        dataFields: [
          "firstName: string",
          "lastName: string",
          "productName: string",
          "quantity: number",
          "price: number",
          "total: number",
        ],
      });
    })
    .catch(() => ({ data: [], length: 0 }));
});
</script>
<template>
  <p>Smart.Grid bound to a server via AJAX</p>
  <smart-grid ref="gridRef" id="grid"></smart-grid>
</template>
<style scoped>
smart-grid {
  width: 100%;
}
</style>

More demos of our Smart.Grid you can find here