26

I am using electron and am trying to open a file browser when a user clicks on button. From the render process I am trying to include the elctron.dialog package like this.

const dialog = require( 'electron' ).dialog;

console.log( dialog );

However the result from the console log is undefined

I am absolutely sure I am in the rendering process so I am not sure why this is not working. The documentation suggests that this is the correct way of doing things but it appears to not be working.

This is my package.json file

{
  "name": "my-app",
  "version": "0.1.0",
  "main": "./main.js",
  "scripts": {
    "start": "electron ."
  },
  "dependencies": {
    "electron": "^0.4.1"
  }
}

This is my main.js file

    'use strict';

    var app = require( 'app' );
    var BrowserWindow = require( 'browser-window' );
    var ipc = require( 'ipc' );

    var mainWindow = null;

    app.on(
        'ready', function () {
            mainWindow = new BrowserWindow(
                {
                    frame : true,
                    height: 700,
                    width : 500
                }
            );

            mainWindow.loadUrl( 'file://' + __dirname + '/app/index.html' );

            mainWindow.openDevTools();
            mainWindow.on(
                'closed', function () {
                    mainWindow = null;
                }
            );

        }
    );

    ipc.on(
        'close-main-window', function () {
            app.quit();
        }
    );

this is the rendered process file

    // Add your index.js code in this file
    var ipc = require( 'ipc' );

    const dialog = require( 'electron' ).dialog;

    console.log( dialog );

This is the console

Is this incorrect?

Subtubes
  • 15,851
  • 22
  • 70
  • 105

8 Answers8

35

On the Renderer process, you must use the Remote module.

const dialog = require('electron').remote.dialog 

More info:
Electron Dialog API
Electron Remote API

Philip
  • 5,011
  • 2
  • 30
  • 36
12

I'm surprised none of the answers mention this yet, but since the remote module has been removed and it's preferable not to use the userland remote for a variety of reasons discussed here, you'll want to use ipc to have the main process show the dialog.

renderer.js:

const { ipcRenderer } = require("electron");
ipcRenderer.invoke("showDialog", "message");

main.js:

const { ipcMain, dialog } = require("electron");
ipcMain.handle("showDialog", (e, message) => {
    dialog.showMessageBox(mainWindow, { message });
});
pushkin
  • 9,575
  • 15
  • 51
  • 95
  • 2
    it is insanely hard to find this solution considering it is so simple – Nestoro Mar 10 '22 at 18:14
  • Ditto @Nestoro. This got me out of a fix. – Manngo Apr 10 '22 at 00:24
  • I had a similar approach in my project, but it didn’t work properly. You have two differences which helped: (a) `ipcRenderer.invoke() - I had `sendSync` which seems to hang; (b) `ipcMain.handle()` - I had `ipcMain.on()`: I don’t know that also makes a difference. Thanks. – Manngo Apr 10 '22 at 00:27
  • `handle` and `invoke` go together and are the newer APIs. They're similar to `send` and `on`. Maybe `send`/`on` would work fine for you @Manngo – pushkin Apr 10 '22 at 01:02
  • @pushkin No, `send`/`on` seemed to be crashing. I was also having problems with writing to a file afterwards, but I could fix that using the `invoke` promise. Thanks again. – Manngo Apr 10 '22 at 01:44
6

Electron in the newest version have changed the way of requiring the modules. The modules are encapsulated within the electron Name space.

// for getting the electrons modules here the new method now i'm using 1.7.12 Electron version (i don't know what that will be in the future)
// you require electron first! it's a name space (module)
var electron = require("electron");
var remote = electron.remote; // you get all the subModuls directly as property (cool incapsulation)
//remote = require("remote") ===> will not work!!!!

// for dialog it's the same !! but we now use remote as modul
var dialog = remote.dialog;

Also you can use this syntax, to import several modules with less writing and by gathering them all together:

var {remote, ipcRenderer, someOtherModulFromElectron} = electron;

for example in the main.js (main process) we could write such a call:

const electron = require('electron')
const {app, BrowserWindow, Menu} = electron;

in place of :

const electron = require('electron')

// Module to control application life.
const app = electron.app

// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

//modul for bar menu
const Menu = electron.Menu
Mohamed Allal
  • 17,920
  • 5
  • 94
  • 97
5

Accroding to https://github.com/electron/remote
The remote module was deprecated in Electron 12, @electron/remote is the replacement.

$ npm install --save @electron/remote

In main.js:

require('@electron/remote/main').initialize()

I also have to set few webPreferences for BrowserWindow

  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true,
      contextIsolation: false,
      preload: path.join(__dirname, 'preload.js')
    }
  })

In renderer.js:

const {dialog} = require('@electron/remote')

function showError() {
    dialog.showErrorBox(...)
}

After that dialog is working for me.

Justin
  • 183
  • 3
  • 7
3

After a few hours of looking into it someone else pointed out to me that the "new" way (4/15/16) of doing this is the following.

var remote = require('remote');
var dialog = remote.require('dialog');

dialog.showOpenDialog({ 
  properties: [ 'openFile' ] }, function ( filename ) {
    console.log( filename.toString() );
  }
);

You must require remote and then from remote require dialog. It looks like you no longer need to require electron

Haroen Viaene
  • 1,329
  • 18
  • 33
Subtubes
  • 15,851
  • 22
  • 70
  • 105
2

The latest version of electron made some change to dialog call for security, like you can call dialog in the main process (like index.js) but you can't call direct in the renderer process (like render.js), so have to use ipc

In this example: the main process handles dialog through a preload and renders access dialog by calling the main process

resources

index.js

const createWindow = () => {
    const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      contextIsolation: true,
      preload: path.join(__dirname, "preload.js"),
    },
});

...

app.whenReady().then(() => {
    ipcMain.handle("dialog", async (event, method, params) => {
        const result = await dialog[method](params);
        return result;
    });
});

preload.js

const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electron", {
  openDialog: (method, config) => ipcRenderer.invoke("dialog", method, config),
});

render.js

 const dialogConfig = {
  title: "Select a file",
  buttonLabel: "This one will do",
  properties: ["openFile"],
};
const { canceled, filePaths } = await electron.openDialog("showOpenDialog",dialogConfig);
console.log(canceled, filePaths);
mmblack
  • 31
  • 2
  • 5
1

This code works in the script of the html file :

const remote = require('electron').remote 

const dialog = remote.dialog;

dialog.showErrorBox('Error title', 'error')
D.Richard
  • 109
  • 1
  • 7
1

For me it was because I was using node index.js. What worked instead was using npm start while having this in package.json:

  "scripts": {
    "start": "electron index.js"
  },
Sámal Rasmussen
  • 2,887
  • 35
  • 36