Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Using AG Grid in Electron Applications
AG Grid profile imageAlan Richardson
Alan Richardson forAG Grid

Posted on • Originally published atblog.ag-grid.com on

Using AG Grid in Electron Applications

Using AG Grid in Electron Applications

This post contributed to the AG Grid blog byArek Nawo

Electron is a cross-platform framework for building native desktop applications with web technologies. It’s built on top of Node.js and theChromium browser, allowing you to use all of the latest features and improvements from the browser and JavaScript language.

Electron enables you to create the best user experience while taking care of all the complex parts of making a native app. With Electron, you can reuse both your knowledge and codebase across the web and all desktop platforms. If you already know how to develop frontend web apps and Node.js backends, you’ll feel right at home with Electron as it basically combines those two into a single app.

In this article, you’ll learn how to integrateAG Grid—an advanced and performant JavaScript grid library—into your Electron app. You’ll create a simple to-do app with native functionality to save and restore its state from a JSON file.

You can follow along withthis GitHub repo.

How Does Electron Work?

A basic Electron app consists of two processes.The main process acts as an entry point for the app, with access to Node.js APIs and modules (including native ones). It also controls the app’s lifecycle and manages its windows using Electron-provided APIs.

So while the main process is like your web app’s backend,the renderer process is more like its frontend. It’s responsible for rendering the app’s UI and runs in every opened window. Thus, it should follow web standards (such asHTML5,CSS3, andECMAScript) and use browser Web APIs. No Node-specific code is allowed.

To communicate between processes (e.g., to handle data transfer or invoke native functionality from the UI), you can usepreload scripts andinter-process communication (IPC). Preload scripts run in the renderer process before the main script and have access to the Node.js API. You can use them with the Electron’scontextBridge module to safely expose privileged APIs to the renderer process. Most notably, you can expose helpers leveraging theipcRenderer module to communicate with the main process.

Using AG Grid with Electron

A good way to get started with Electron is withElectron Forge—“a complete tool for creating, publishing, and installing modern Electron applications.” Electron Forge has everything you need to work on your Electron app, including aWebpack-powered template.

Setting Up the Project

To initiate a new project with Electron Forge, ensure you have git and Node.js v12.13.0 or newer installed. Then, run the following commands to create the project, install additional dependencies, and start the development server:

npx create-electron-app@latest project --template=webpackcd projectnpm install ag-grid-communitynpm run start
Enter fullscreen modeExit fullscreen mode

Managing the App’s Window

By default, the template includes files for main and renderer processes. Inside thesrc/main.js file, you’ll see the starting point of your app. Take a closer look at thecreateWindow() function and event listeners below:

const { app, BrowserWindow } = require("electron");// ...const createWindow = () => {  const mainWindow = new BrowserWindow();  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);  mainWindow.webContents.openDevTools();};app.on("ready", createWindow);app.on("window-all-closed", () => {  if (process.platform !== "darwin") {    app.quit();  }});app.on("activate", () => {  if (BrowserWindow.getAllWindows().length === 0) {    createWindow();  }});
Enter fullscreen modeExit fullscreen mode

Theapp module controls your app’s event lifecycle. When the app is ready, it creates a new window by callingcreateWindow(), which in turn creates a new instance ofBrowserWindow. Using theloadURL() method, the window loads the content served by Webpack’s development server. It also opens dev tools for easier debugging using thewebContents.openDevTools() method.

The other event listeners handle macOS-specific edge cases, like keeping the app open without any windows (window-all-closed) or opening a new window when activated from the dock (activate).

Adding Preload Script

To allow native API access from the renderer process, you’ll have to expose some functions in the preload script. Although the template doesn’t include it by default, adding it yourself is easy.

Create a newsrc/preload.js file and edit theconfig.forge.plugins field inpackage.json to inform Electron Forge about the preload script:

{  /* ... */  "config": {    "forge": {      "packagerConfig": {},      /* ... */      "plugins": [        [          "@electron-forge/plugin-webpack",          {            "mainConfig": "./webpack.main.config.js",            "renderer": {              "config": "./webpack.renderer.config.js",              "entryPoints": [                {                  "html": "./src/index.html",                  "js": "./src/renderer.js",                  "preload": {                    "js": "./src/preload.js"                  },                  "name": "main_window"                }              ]            }          }        ]      ]    }  }  /* ... */}
Enter fullscreen modeExit fullscreen mode

To initialize the script, specify it when creating aBrowserWindow in thesrc/main.js file using the Webpack-provided global variable:

// ...const createWindow = () => {  const mainWindow = new BrowserWindow({    webPreferences: {      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,    },  });  // ...};// ...
Enter fullscreen modeExit fullscreen mode

Building the Renderer Process UI

With essential window management ready, you can start working on the renderer process. You’ll use AG Grid to create a simple to-do list with the ability to add, remove, and mark items as complete.

Developing a renderer process is very similar to creating a frontend web application. You can use all the frontend frameworks and APIs available in the browser environment. In this tutorial, you’ll use plain HTML, JS, and CSS to keep things simple.

Start by creating your UI’s structure in thesrc/index.html file:

<!DOCTYPE html><html>  <head>    <meta charset="UTF-8" />    <title>AG Grid + Electron</title>  </head>  <body>    <div>      <h1>Electron TO-DO</h1>      <div>        <button>Save</button>        <button>Restore</button>        <button>Add</button>      </div>      <div></div>      <div></div>    </div>  </body></html>
Enter fullscreen modeExit fullscreen mode

Then, add the necessary styling insrc/index.css :

html,body {  height: 100%;  margin: 0;}#grid {  width: 100%;  flex: 1;}.container {  margin: auto;  display: flex;  justify-content: center;  align-items: center;  flex-direction: column;  height: calc(100% - 2rem);  padding: 1rem;  width: 30rem;}.btn-container {  display: grid;  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));  gap: 0.5rem;  width: 100%;}.divider {  margin: 0.5rem 0;  background: #e6e6e6;  height: 0.125rem;}.checkbox {  height: 100%;  margin: 0;  width: 1.25rem;}.btn {  flex: 1;  padding: 0.5rem;  background: #6b7280;  border: none;  color: #fff;  font-size: 1rem;}.btn:hover {  background: #9ca3af;  cursor: pointer;}.add-btn {  padding: 0.5rem;  background: #f97316;  border: none;}.add-btn:hover {  background: #fb923c;  cursor: pointer;}.remove-btn {  display: inline-flex;  max-height: 1.25rem;  max-width: 1.25rem;  font-size: 1.25rem;  justify-content: center;  align-items: center;  background-color: #ef4444;}.remove-btn:hover {  background-color: #f87171;}
Enter fullscreen modeExit fullscreen mode

With this setup, you can now move to thesrc/renderer.js file to initialize the grid:

import "ag-grid-community/dist/styles/ag-grid.css";import "ag-grid-community/dist/styles/ag-theme-alpine.css";import "./index.css";import { Grid } from "ag-grid-community";let rowData = [];const columnDefs = [  // ...];const gridOptions = {  columnDefs,  rowData,};const saveBtn = document.getElementById("save-btn");const restoreBtn = document.getElementById("restore-btn");const addBtn = document.getElementById("add-btn");const addTodo = () => {  // ...};const removeTodo = (rowIndex) => {  // ...};const saveToFile = () => {  // ...};const restoreFromFile = async () => {  // ...};const setupGrid = () => {  const gridDiv = document.getElementById("grid");  new Grid(gridDiv, gridOptions);  addBtn.addEventListener("click", addTodo);  saveBtn.addEventListener("click", saveToFile);  restoreBtn.addEventListener("click", restoreFromFile);};document.addEventListener("DOMContentLoaded", setupGrid);
Enter fullscreen modeExit fullscreen mode

All setup, including creating aGrid instance and adding event handlers, happens after the DOM is loaded. The grid is created using the provided configuration, defining its columns and input data:

// ...let rowData = [];const columnDefs = [  { field: "task", editable: true, flex: 1 },  {    field: "completed",    width: 120,    cellRenderer(params) {      const input = document.createElement("input");      input.type = "checkbox";      input.checked = params.value;      input.classList.add("checkbox");      input.addEventListener("change", (event) => {        params.setValue(input.checked);      });      return input;    },  },  {    field: "remove",    width: 100,    cellRenderer(params) {      const button = document.createElement("button");      button.textContent = "✕";      button.classList.add("btn", "remove-btn");      button.addEventListener("click", () => removeTodo(params.rowIndex));      return button;    },  },];// ...
Enter fullscreen modeExit fullscreen mode

The columns are defined by certain parameters such asfield name,width, or customcellRenderer in case you want to display the data differently.editable enables built-in editing support, allowing the user to change the task’s name. At the same time,flex is an alternative towidth, indicating that the column should fill the remaining space.

For“completed” and“remove” columns, custom cell renderers render a checkbox and button to, respectively, change the status of the task or remove it from the list entirely. The actual change to the grid’s data is done withparams.setValue() and a separateremoveTodo() function.

BothaddTodo() andremoveTodo() operate using thegridOptions.api object. After being provided to the grid,gridOptions gets theapi property to allow control of the grid:

// ...const addTodo = () => {  rowData = [...rowData, { task: "New Task", completed: false }];  gridOptions.api.setRowData(rowData);};const removeTodo = (rowIndex) => {  rowData = rowData.filter((value, index) => {    return index !== rowIndex;  });  gridOptions.api.setRowData(rowData);};// ...
Enter fullscreen modeExit fullscreen mode

Values fromrowData items are also bound to the grid, meaning that if the user changes the completion status or the task’s name, the new value is reflected in one of therowData items.

With all these changes, the app’s UI is now fully functional and looks like this:

Using AG Grid in Electron Applications

All that’s left is to implement save and restore functionality. For that, you’ll have to return to the main process.

Adding Native Functionality

Inside thesrc/main.js file, create a new function,handleCommunication(), for handling IPC implementation:

const { app, BrowserWindow, ipcMain, dialog } = require("electron");// ...const handleCommunication = () => {  ipcMain.removeHandler("save-to-file");  ipcMain.removeHandler("restore-from-file");  ipcMain.handle("save-to-file", async (event, data) => {    try {      const { canceled, filePath } = await dialog.showSaveDialog({        defaultPath: "todo.json",      });      if (!canceled) {        await fs.writeFile(filePath, data, "utf8");        return { success: true };      }      return {        canceled,      };    } catch (error) {      return { error };    }  });  ipcMain.handle("restore-from-file", async () => {    try {      const { canceled, filePaths } = await dialog.showOpenDialog({        properties: ["openFile"],        filters: [          {            name: "json",            extensions: ["json"],          },        ],      });      if (!canceled) {        const [filePath] = filePaths;        const data = await fs.readFile(filePath, "utf8");        return { success: true, data };      } else {        return { canceled };      }    } catch (error) {      return { error };    }  });};// ...
Enter fullscreen modeExit fullscreen mode

First, usingipcMain.removeHandler(), ensure that no existing handlers are attached to the used channels in case the window was reactivated (macOS-specific). TheipcMain.handle() method allows you to handle specific events and respond with data by simply returning a value from the handler.

For this app, the channels used are”save-to-file” and”restore-from-file”. Their handlers use thedialog module to bring up the system’s native open or save dialogues. The resulting paths are then provided to Node.js’s built-infs module to read from or write to the provided file.

handleCommunication() should be called from thecreateWindow() function:

// ...const createWindow = () => {  const mainWindow = new BrowserWindow({    webPreferences: {      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,    },  });  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);  mainWindow.webContents.openDevTools();  handleCommunication();};// ...
Enter fullscreen modeExit fullscreen mode

To be able to send an IPC message from the renderer process, you’ll have to use the preload script and thecontextBridge module:

// src/preload.jsconst { contextBridge, ipcRenderer } = require("electron");contextBridge.exposeInMainWorld("electronAPI", {  saveToFile(data) {    return ipcRenderer.invoke("save-to-file", data);  },  restoreFromFile() {    return ipcRenderer.invoke("restore-from-file");  },});
Enter fullscreen modeExit fullscreen mode

contextBridge.exposeInMainWorld() safely exposes the provided API to the renderer process. Keep in mind that, in preload script, you’ve got access to privileged APIs, which, for security reasons, should be freely available from the frontend of your Electron app.

The exposed methods use theipcRenderer module to send messages to theipcMain listener on the other process. In the case of the”save-to-file” channel, additional data in the form of a JSON string to save is provided.

With the bridge ready, you can return to the renderer process and finish the integration by adding proper handlers for the last two buttons:

// ...const saveToFile = () => {  window.electronAPI.saveToFile(JSON.stringify(rowData));};const restoreFromFile = async () => {  const result = await window.electronAPI.restoreFromFile();  if (result.success) {    rowData = JSON.parse(result.data);    gridOptions.api.setRowData(rowData);  }};// ...
Enter fullscreen modeExit fullscreen mode

Each handler uses methods from theelectronAPI object available on thewindow. InsaveToFile(), therowData is stringified and sent to the main process for writing it to the file. In the case of the restore operation, it firstawaits the file’s stringified content to then parse and assign it to the grid.

Now your app can use the native file dialog to restore and save its state:

Using AG Grid in Electron Applications

The final app outputs a JSON file like the following:

[{"task":"Learn Electron","completed":true},{"task":"Learn AG Grid","completed":false}]
Enter fullscreen modeExit fullscreen mode

Conclusion

Now you know how to use AG Grid in an Electron app. You’ve learned about Electron processes—what they are and how they work. Finally, you established IPC to communicate between them to implement native functionality into your app.

As you’ve seen, thanks to Electron, you can quickly transform your web app into an installable native desktop app with all the functionality that comes alongside it.

AG Grid is a high-performance JavaScript table library that’s easy to set up. It integrates well with your favorite frameworks, like React, and works great with other parts of the JavaScript ecosystem, such as Electron. Check outthe official documentation to learn more.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Our mission is simple: Build the best datagrid in the world

AG Grid has support for React, Angular, Vue, JavaScript and Typescript. Full demos of the grid in action on our site, including embedded charts.

More fromAG Grid

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp