Electron: Prompt and modal dialog box

Creating a modal dialog box to interact with the user.

It is possible to use the alert and confirm functions in an application with Electron. But the lack of support for the prompt dialog, and the intention of never implementing it, forces us to create a substitution widget because it is sometimes indispensable in an application to ask the user for information before continuing the processing.
It is also necessary that the dialog is modal so that the keyboard commands are active in the dialog box and not in the application.

To create this dialog we use the BrowserWindow object and an HTML page defining the contents of the window. It would be simpler to create the dialog in the application window, but it is not possible. To be modal, a window must indicate - by its definition - the parent window. It must therefore be in the main process because it seems that a renderer process window can not have a parent in the main process, and the application window opens in the main process.

We also use Electron's IPC communication protocol which is better suited than WebSocket thanks to its synchronous mode. See IPC vs. WebSocket for a comparison of the two protocols.

Source code of the prompt dialog

This HTML page is loaded in the BrowserWindows window used to create the dialog box, and it defines the contents of this dialog box.

<body>
<p id="label"></p>
<p>
  <input type = "text" id="data" value="">
  <input type = "button" id="ok" value="OK" onclick="response()">
  <input type = "button" value="Cancel" onclick="cancel()">
</p>

<script>
const { ipcRenderer } = require("electron")
function cancel() {
  ipcRenderer.send("closeDialog", "")
  this.close();
}

function response() {
  ipcRenderer.send("closeDialog", document.getElementById("data").value)
  this.close();
}

window.onload=function() {
  var options = ipcRenderer.sendSync("openDialog", "")
  var params = JSON.parse(options)
  document.getElementById("label").innerHTML = params.label;
  document.getElementById("data").value = params.value;
  document.getElementById("ok").value = params.ok;
}
</script>
</body>

When the page is loaded, customization elements are asked to the backend: A label, a default value for the text entry field, and the label of the action button.
The title of the dialog is given by the backend.

When the OK button is clicked, the content typed by the user is transmitted to the backend via the "closeDialog" channel.
When you click Cancel, you send an empty string.

Source code of the backend in ipcMain side

The dialog box is created in the main process, next to the application window to be defined as its child.

var promptWindow;
var promptOptions
var promptAnswer;

// Creating the dialog

function promptModal(parent, options, callback) {
  promptOptions = options;
  promptWindow = new BrowserWindow({
    width:360, height: 120, 
    'parent': parent,
    'show': false,
    'modal': true,
    'alwaysOnTop' : true, 
    'title' : options.title,
    'autoHideMenuBar': true,
    'webPreferences' : { 
      "nodeIntegration":true,
      "sandbox" : false 
    }   
  });
  promptWindow.on('closed', () => { 
    promptWindow = null 
    callback(promptAnswer);
  })

  // Load the HTML dialog box
  promptWindow.loadURL(path.join(__dirname, "prompt.html"))
  promptWindow.once('ready-to-show', () => { promptWindow.show() })
}

// Called by the dialog box to get its parameters

ipcMain.on("openDialog", (event, data) => {
    event.returnValue = JSON.stringify(promptOptions, null, '')
})

// Called by the dialog box when closed

ipcMain.on("closeDialog", (event, data) => {
  promptAnswer = data
})

// Called by the application to open the prompt dialog

ipcMain.on("prompt",  (event, notused) => {
	promptModal(win, {
	    "title": "Prompt demo",
	    "label":"Fill this input field:", 
	    "value":"example", 
	    "ok": "ok"
	    }, 
	    function(data) {
        event.returnValue = data
      }
    );        
});

The "openDialog" channel is created by the page of the dialog when loading, to retrieve the parameters of the box.
The "closeDialog" channel is opened to return the value entered by the user, or an empty string if it cancel the operation.

A third channel, "prompt" must also be opened, this time by the application, to control the opening of the dialog. Like the previous two, it is synchronous since the window is modal and interrupts any other operation.

In callback, the "prompt" channel calls the promptModal function that creates the dialog box. The parent win window is specified. The promptModal function itself has a callback that returns to the application that opened the "prompt" channel the value entered by the user.

Source code of the application using the prompt dialog

This HTML page is loaded in the main process window.

<body>
  <h1>Prompt demo</h1>
  <p>
    <input type="button" value="Get a value" onclick="ePrompt()">
  </p>
  <fieldset><div id="answer"></div></fieldset>

<script>
const { ipcRenderer } = require("electron")
function ePrompt() {
  var answer = ipcRenderer.sendSync("prompt", "")
  if(answer != "") 
    document.getElementById("answer").innerHTML = answer;
} </script> </body>

This demonstration program is quite simple: when you click on the button, it calls the ePrompt () function. This opens the synchronous channel "prompt" with the backend, which commands the latter to open the dialog.
The user response is the return value of the sendSync function of ipcRenderer.
This value is then displayed in a fieldset.

Download

The full code of the demo is available in an archive.

To launch the application, go to the electron-prompt directory and type:

electron prompt.js

See also: HTML dialog tag and modal window for Electron. Replaces prompt() or any other dialog, with a simple HTML tag.