Native Modules in Electron application
Working with native Node.js modules in Electron extends your application's capabilities beyond what's possible with web technologies alone. However, it's crucial to remember that native modules run in the main process, not the renderer process (where your Angular app typically resides). Communication between the processes is essential.
Here are examples demonstrating the use of various native modules, emphasizing the necessary IPC for interaction with the renderer process (your Angular application):
- 1. File System Access (fs module):
- • Main Process:
- • Renderer Process (Angular Service):
- 2. Network Operations (http or https modules) or node-fetch:
- • Main Process:
- • Renderer Process: Similar to the readFile example above, use ipcRenderer.invoke to call the fetch handler in the main process.
- 3. Executing Shell Commands (child_process module):
- • Main Process:
- • Renderer Process: Use ipcRenderer.invoke to call the main process. Handle any errors returned from the main process.
- 4. Accessing System Information (os module):
- • Main Process:
- • Renderer Process: Retrieve data via ipcRenderer.invoke.
- 5. Working with Directories (fs module):
- • Main Process: Create a directory asynchronously:
- • Renderer Process: Use ipcRenderer.invoke to request directory creation.
- 6. Using a Database (e.g., better-sqlite3):
- • Main Process:
- • Renderer Process: Call the main process's handler.
- 7. Managing Tray Icons (Tray module):
- • Main Process: Create and manage a tray icon. This is a main-process-only task. You would need to handle clicks, menu items, etc., within the main process. This is a more complex example and not directly tied to Angular
- 8. Printing (webContents API): While webContents is available on the renderer, it is usually used in the main process to trigger a print dialogue.
- 9. Clipboard Access (clipboard module): Accessing the clipboard requires IPC to ensure security
- 10. Serial Port Communication (serialport):
- 1. Main Process: Import the necessary Node.js module, create an ipcMain event handler to respond to requests from the renderer. Handle asynchronicity (promises or callbacks).
- 2. Renderer Process: Use ipcRenderer.invoke (for promises) or ipcRenderer.send/ipcRenderer.on (for callbacks) to communicate with the main process. Angular's HttpClient often simplifies async network requests.
1: import { ipcMain } from 'electron';
2: import * as fs from 'node:fs/promises'; // Use promises for async operations
3:
4: ipcMain.handle('readFile', async (event, filePath) => {
5: try {
6: const contents = await fs.readFile(filePath, 'utf-8');
7: return contents;
8: } catch (error) {
9: console.error('Error reading file:', error);
10: return null; // Or throw error
11: }
12: });
1: import { Injectable } from '@angular/core';
2: import { ipcRenderer } from 'electron';
3:
4: @Injectable({ providedIn: 'root' })
5: export class FileService {
6: async readFile(filePath: string): Promise<string | null> {
7: return await ipcRenderer.invoke('readFile', filePath);
8: }
9: }
1: import { ipcMain } from 'electron';
2: import fetch from 'node-fetch'; //Requires: npm install node-fetch
3:
4: ipcMain.handle('fetch', async (event, url) => {
5: try {
6: const response = await fetch(url);
7: const data = await response.json();
8: return data;
9: } catch (error) {
10: console.error('Fetch error:', error);
11: return null;
12: }
13: });
1: import { ipcMain } from 'electron';
2: import { exec } from 'node:child_process';
3:
4: ipcMain.handle('executeCommand', async (event, command) => {
5: return new Promise<string>((resolve, reject) => {
6: exec(command, (error, stdout, stderr) => {
7: if (error) reject(error);
8: else resolve(stdout);
9: });
10: });
11: });
1: import { ipcMain } from 'electron';
2: import os from 'node:os';
3:
4: ipcMain.handle('getSystemInfo', (event) => {
5: return {
6: platform: os.platform(),
7: release: os.release(),
8: totalmem: os.totalmem(),
9: // ... other system info
10: };
11: });
1: import { ipcMain } from 'electron';
2: import * as fs from 'node:fs/promises';
3:
4: ipcMain.handle('createDirectory', async (event, dirPath) => {
5: try {
6: await fs.mkdir(dirPath, { recursive: true });
7: return true;
8: } catch (error) {
9: console.error("Error creating directory:", error);
10: return false;
11: }
12: });
1: import { ipcMain } from 'electron';
2: import { Database } from 'better-sqlite3'; // Requires: npm install better-sqlite3
3:
4: ipcMain.handle('getDatabaseData', async (event, query) => {
5: const db = new Database('./mydb.db');
6: try {
7: const rows = db.prepare(query).all();
8: db.close();
9: return rows;
10: } catch (error) {
11: db.close();
12: console.error('Database error:', error);
13: return null;
14: }
15: });
Remember that all these examples involve asynchronous operations. Using async/await improves readability. Always handle errors in a way that's suitable for your application. Proper error handling prevents crashes and improves the user experience. Thorough testing is essential to ensure reliability.
Electron context:
AngularElectron context:
|