i am beginner in desktop apps development with electron, when i try to send message from preload script to main.js using ipcRenderer and ipcMain in main.js below details shows up in VScode terminal and nothing happens(no event occurs) not even console.log works
{
preventDefault: [Function: preventDefault],
sender: EventEmitter {
isDestroyed: [Function: isDestroyed],
destroy: [Function: destroy],
getBackgroundThrottling: [Function: getBackgroundThrottling],
setBackgroundThrottling: [Function: setBackgroundThrottling],
getProcessId: [Function: getProcessId],
getOSProcessId: [Function: getOSProcessId],
equal: [Function: equal],
_loadURL: [Function: _loadURL],
reload: [Function: reload],
reloadIgnoringCache: [Function: reloadIgnoringCache],
downloadURL: [Function: downloadURL],
getURL: [Function: getURL],
getTitle: [Function: getTitle],
isLoading: [Function: isLoading],
isLoadingMainFrame: [Function: isLoadingMainFrame],
isWaitingForResponse: [Function: isWaitingForResponse],
stop: [Function: stop],
canGoBack: [Function: canGoBack],
goBack: [Function: goBack],
canGoForward: [Function: canGoForward],
goForward: [Function: goForward],
canGoToOffset: [Function: canGoToOffset],
goToOffset: [Function: goToOffset],
canGoToIndex: [Function: canGoToIndex],
goToIndex: [Function: goToIndex],
getActiveIndex: [Function: getActiveIndex],
clearHistory: [Function: clearHistory],
length: [Function: length],
isCrashed: [Function: isCrashed],
forcefullyCrashRenderer: [Function: forcefullyCrashRenderer],
setUserAgent: [Function: setUserAgent],
getUserAgent: [Function: getUserAgent],
savePage: [Function: savePage],
openDevTools: [Function: openDevTools],
closeDevTools: [Function: closeDevTools],
isDevToolsOpened: [Function: isDevToolsOpened],
isDevToolsFocused: [Function: isDevToolsFocused],
enableDeviceEmulation: [Function: enableDeviceEmulation],
disableDeviceEmulation: [Function: disableDeviceEmulation],
toggleDevTools: [Function: toggleDevTools],
inspectElement: [Function: inspectElement],
setIgnoreMenuShortcuts: [Function: setIgnoreMenuShortcuts],
setAudioMuted: [Function: setAudioMuted],
isAudioMuted: [Function: isAudioMuted],
isCurrentlyAudible: [Function: isCurrentlyAudible],
undo: [Function: undo],
redo: [Function: redo],
cut: [Function: cut],
copy: [Function: copy],
paste: [Function: paste],
pasteAndMatchStyle: [Function: pasteAndMatchStyle],
delete: [Function: delete],
selectAll: [Function: selectAll],
unselect: [Function: unselect],
replace: [Function: replace],
replaceMisspelling: [Function: replaceMisspelling],
findInPage: [Function: findInPage],
stopFindInPage: [Function: stopFindInPage],
focus: [Function: focus],
isFocused: [Function: isFocused],
sendInputEvent: [Function: sendInputEvent],
beginFrameSubscription: [Function: beginFrameSubscription],
endFrameSubscription: [Function: endFrameSubscription],
startDrag: [Function: startDrag],
attachToIframe: [Function: attachToIframe],
detachFromOuterFrame: [Function: detachFromOuterFrame],
isOffscreen: [Function: isOffscreen],
startPainting: [Function: startPainting],
stopPainting: [Function: stopPainting],
isPainting: [Function: isPainting],
setFrameRate: [Function: setFrameRate],
getFrameRate: [Function: getFrameRate],
invalidate: [Function: invalidate],
setZoomLevel: [Function: setZoomLevel],
getZoomLevel: [Function: getZoomLevel],
setZoomFactor: [Function: setZoomFactor],
getZoomFactor: [Function: getZoomFactor],
getType: [Function: getType],
_getPreloadPaths: [Function: _getPreloadPaths],
getLastWebPreferences: [Function: getLastWebPreferences],
getOwnerBrowserWindow: [Function: getOwnerBrowserWindow],
inspectServiceWorker: [Function: inspectServiceWorker],
inspectSharedWorker: [Function: inspectSharedWorker],
inspectSharedWorkerById: [Function: inspectSharedWorkerById],
getAllSharedWorkers: [Function: getAllSharedWorkers],
_print: [Function: _print],
_printToPDF: [Function: _printToPDF],
_setNextChildWebPreferences: [Function: _setNextChildWebPreferences],
addWorkSpace: [Function: addWorkSpace],
removeWorkSpace: [Function: removeWorkSpace],
showDefinitionForSelection: [Function: showDefinitionForSelection],
copyImageAt: [Function: copyImageAt],
capturePage: [Function: capturePage],
setEmbedder: [Function: setEmbedder],
setDevToolsWebContents: [Function: setDevToolsWebContents],
getNativeView: [Function: getNativeView],
incrementCapturerCount: [Function: incrementCapturerCount],
decrementCapturerCount: [Function: decrementCapturerCount],
isBeingCaptured: [Function: isBeingCaptured],
setWebRTCIPHandlingPolicy: [Function: setWebRTCIPHandlingPolicy],
getWebRTCIPHandlingPolicy: [Function: getWebRTCIPHandlingPolicy],
takeHeapSnapshot: [Function: takeHeapSnapshot],
setImageAnimationPolicy: [Function: setImageAnimationPolicy],
_getProcessMemoryInfo: [Function: _getProcessMemoryInfo],
id: 1,
session: [Getter],
hostWebContents: [Getter],
devToolsWebContents: [Getter],
debugger: [Getter],
mainFrame: [Getter],
_windowOpenHandler: null,
_events: [Object: null prototype] {
'-ipc-message': [Function (anonymous)],
'-ipc-invoke': [Function (anonymous)],
'-ipc-message-sync': [Function (anonymous)],
'-ipc-ports': [Function (anonymous)],
crashed: [Function (anonymous)],
'render-process-gone': [Function (anonymous)],
'devtools-reload-page': [Function (anonymous)],
'-new-window': [Function (anonymous)],
'-will-add-new-contents': [Function (anonymous)],
'-add-new-contents': [Function (anonymous)],
login: [Function (anonymous)],
'ready-to-show': [Function (anonymous)],
'select-bluetooth-device': [Function (anonymous)]
},
_eventsCount: 13
},
frameId: 1,
processId: 4,
reply: [Function (anonymous)]
}
on button click a function from preload will be called which will send message to main processs
renderer.js
button.addEventListener('click', function() {
window.LoadProductWindow.load();
});
preload.js
const {
contextBridge,
electron,
ipcRenderer
} = require('electron')
contextBridge.exposeInMainWorld('LoadProductWindow', {
load: () => ipcRenderer.send('load_product_window')
})
main.js
ipcMain.on('load_product_window', () => {
console.log('received')
});
please solve my issue and also i want to add a new window on button click to add new items how can i do that?
Thanks
There is no doubt that many examples showing the use of
Electron's Inter-Process Communication and preload.js
are
complicated and difficult to understand. Once you have a firm understanding
of Context-Isolation then things will begin to fall
into place.
The main issue I have been finding recently is that many people are breaking
the Separation of Concerns design principle with
their preload.js
script. Whilst this is not a hard and fast rule, simplifying your preload.js
script by moving all
logic out of it apart from pure communication logic makes for really simple, readable code.
First off, let's define a simple, easy to read preload.js
script.
In this script you will find an ipc
object containing 3 arrays (send
, receive
and sendReceive
) where you can
define your white-listed channels names. When called from either the main
thread or render thread, they will pass without be obstructed. Any and all other channel names used which are not
defined within these 3 arrays will be blocked.
Here, I have added the channel name productWindow:load
to the send
(from render to main thread) array.
preload.js
(main thread)
'use strict';
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'productWindow:load'
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Next, we will quickly define your index.html
file which will include Javascript functionality to send an IPC message
to the main thread upon the push of a button.
index.html
(render thread)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Main Page</title>
</head>
<body>
<label for="button">Product Window: </label>
<input type="button" id="button" value="Load Now">
</body>
<script>
document.getElementById('button').addEventListener('click', () => {
window.ipcRender.send('productWindow:load');
})
</script>
</html>
product.html
(render thread)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Product Page</title>
</head>
<body>
<h1>It Works..!</h1>
</body>
</html>
Finally, in your main.js
script (being the entry point of your application) we listen for a message over
the productWindow:load
channel using Electron's ipcMain
module, specifically the ipcMain.on()
method.
main.js
(main thread)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
let mainWindow;
let productWindow;
function createMainWindow() {
const mainWindow = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
mainWindow.loadFile('index.html')
.then(() => { mainWindow.show(); });
return mainWindow;
}
function createProductWindow(parentWindow) {
const productWindow = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
parent: parentWindow,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
productWindow.loadFile('product.html')
.then(() => { productWindow.show(); });
return productWindow;
}
electronApp.on('ready', () => {
mainWindow = createMainWindow();
});
// Let's listen for the 'productWindow:load' signal.
electronIpcMain.on('productWindow:load', () => {
productWindow = createProductWindow(mainWindow);
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createMainWindow();
}
});
As your Electron application begins to grow you will want to begin splitting your main.js
file into smaller files,
to 'separate your concerns'. IE: Moving your different window creation code segments into their own files, including their associated IPC code.
This will improve your code structure, simplify readability and make for a maintainable code base.