'Electron - Uncaught TypeError: Cannot read properties of undefined (reading 'showOpenDialog')
I'm trying to open the dialog on electron, but I'm getting this error:
Uncaught TypeError: Cannot read properties of undefined (reading 'showOpenDialog')
I've already visited several forums and communities to see if I have a solution to my problem. But none resolved perhaps because of the version.
The version of electron I currently use is 16.0.5
This answer didn't help me much
https://stackoverflow.com/a/63756725/14271847
I won't leave all of my main.js, but the part I changed is this one, enableRemoteModule
:
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule:true,
nodeIntegration: true,
contextIsolation: false,
preload: path.join(__dirname, 'preload.js')
}
})
file test.js
And here's what I'm trying to do on electron, following what's in this link
const { dialog } = require('electron')
console.log(dialog.showOpenDialog({ properties: ['openFile', 'multiSelections'] }))
I've tried it with the remote, but it doesn't work:
const { dialog } = require('electron')
console.log(dialog.remote.showOpenDialog({ properties: ['openFile', 'multiSelections'] }))
Could someone help please?
Solution 1:[1]
Enabling Context Isolation and disabling nodeIntegration is best practice nowadays to truly secure your Electron application. Additionally, the use of remote
is discouraged as we now have Inter-Process Communication.
Not knowing what your preload.js
script looks like, I have included a simple preload.js
script below that uses a list of whitelisted channel names and implementation of ipcRenderer
methods only. That said, having a different looking preload.js
script should not change too much the core approach that should be used in
your main.js
and index.html
files to get your dialog working.
Using of the invoke
method will allow us to send a message to the main process to open the dialog and receive a reply, all in one.
preload.js
(main process)
// 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': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': [
'dialog:openMultiFileSelect' // Channel name
]
}
};
// 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);
}
}
}
);
A summary on the use of the above preload.js
script is shown below.
/**
* Render --> Main
* ---------------
* Render: window.ipcRender.send('channel', data); // Data is optional.
* Main: electronIpcMain.on('channel', (event, data) => { methodName(data); })
*
* Main --> Render
* ---------------
* Main: windowName.webContents.send('channel', data); // Data is optional.
* Render: window.ipcRender.receive('channel', (data) => { methodName(data); });
*
* Render --> Main (Value) --> Render
* ----------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
*
* Render --> Main (Promise) --> Render
* ------------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', async (event, data) => {
* return await promiseName(data)
* .then(() => { return result; })
* });
*/
In the main.js
script, using the handle
method, let's listen for a message on the dialog:openMultiFileSelect
channel. Once received, open the dialog.showOpenDialog()
method and .then
wait for a result. Once a result has been received (IE: Files selected and dialog accepted), return the result (via the handle
method) back to the render process.
main.js
(main process)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronDialog = require('electron').dialog;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
electronIpcMain.handle('dialog:openMultiFileSelect', () => {
let options = {
properties: ['openFile', 'multiSelections']
};
return electronDialog.showOpenDialog(window, options)
.then((result) => {
// Bail early if user cancelled dialog
if (result.canceled) { return }
return result.filePaths;
})
})
Finally, in the index.html
file, we listen for a click
event on the button and then send a message via the dialog:openMultiFileSelect
channel to open the dialog (in the main process). Once a result is returned, .then
display the selected file(s).
Note how we detect a result of
undefined
and manage it accordingly. This occurs when the user cancels the dialog.
index.html
(render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
</head>
<body>
<input type="button" id="button" value="Open File Dialog">
<ul id="paths"></ul>
</body>
<script>
document.getElementById('button').addEventListener('click', () => {
window.ipcRender.invoke('dialog:openMultiFileSelect')
.then((paths) => {
if (paths === undefined) { return } // Dialog was cancelled
let result = '';
for (let path of paths) {
result += '<li>' + path + '</li>';
}
document.getElementById('paths').innerHTML = result;
})
})
</script>
</html>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | midnight-coding |