Reputation: 175
I need send element from app.js to renderer.js (index.html) using preload.js, without calling the function by renderer.js.
Is it possible?
Below the electron ipc and contextbridge code.
renderer.js
window.onload = () => {
const counter = document.querySelector('h2');
setInterval(() =>{
api.get(counter.innerText).then(res => {
counter.innerText = res
})
}, 1000)
}
preload.js
const {contextBridge, ipcRenderer} = require('electron')
contextBridge.exposeInMainWorld('api', {
get: arg => ipcRenderer.invoke('get', arg),
})
app.js
const {app, BrowserWindow, ipcMain} = require('electron')
const path = require('path')
app.whenReady().then(() => {
const mainWindow = new BrowserWindow({
width: 500,
height: 500,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// ipc response on request
ipcMain.handle('get', (event, arg) => {
return ++arg
})
mainWindow.loadFile('index.html')
// Open the DevTools.
//mainWindow.webContents.openDevTools()
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>IPC Example</title>
</head>
<body>
<h1>Wait on response...</h1>
<h2>0</h2>
<script src="./renderer.js"></script>
</body>
</html>
Upvotes: 0
Views: 1059
Reputation: 3247
Currently, your preload.js
script is only configured to use ipcRender.invoke()
which is used to communicate from the render thread to the main thread and then back again to the render thread.
If you want to communicate from the main thread to the render thread then you would need to use contents.send(channel, ...args)
.
EG: mainWindow.webContents.send('channel', 'message');
.
To implement 'Separation of Concerns' and keep hard-coded functions out of the preload.js
script, you can refactor your preload.js
script so it only reflects your whitelisted 'channels' and associated transport methods. This also greatly increases readability.
See the below code for changes to you initial IPC invoke function call, implementation of the main thread to render thread IPC and new `preload.js file.
index.html
(render thread)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>IPC Example</title>
</head>
<body>
<h1>Wait on response...</h1>
<h2>0</h2>
</body>
<script src="./renderer.js"></script>
</html>
Use of window.onload
becomes redundant when you place the <script>
tag at the bottom of your html document and not in the <head>
tag.
render.js
(render thread)
const counter = document.querySelector('h2');
// IIFE - Immediately Invoked Function Expression
(function() {
// Let's run the counter function.
counter();
// Listen on this channel for an incoming test.
window.ipcRender.receive('channel:test', (message) => {
console.log(message);
});
})();
//
function counter() {
setInterval(() => {
window.ipcRender.invoke('channel:count', counter.innerText)
.then((result) => {
counter.innerText = result;
})
}, 1000);
}
preload.js
(main thread)
const {contextBridge, ipcRenderer} = require('electron');
// White-listed channels.
const $ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [
'channel:test'
],
// From render to main and back again.
'sendReceive': [
'channel:count'
]
}
};
// 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);
}
}
}
);
app.js
(main thread)
const {app, BrowserWindow, ipcMain} = require('electron');
const path = require('path');
app.whenReady().then(() => {
const mainWindow = new BrowserWindow({
width: 500,
height: 500,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.loadFile('index.html');
// Open the DevTools.
// mainWindow.webContents.openDevTools();
// ipc from render to main and back again.
ipcMain.handle('channel:count', (event, count) => {
return ++count;
})
// ipc from main to render.
mainWindow.webContents.send('channel:test', 'This is a test');
})
Upvotes: 1