1

I am trying to have my communicate between different rendering processes in electron. Rather than have multiple windows I am using electron-tabs which can be found here. To start I simply want my main window to be able to send a message using ipcRenderer to each tab. With multiple windows, you could store each window in a global variable like this stack overflow question and then use the web-contents to send a message.

UPDATE: In the electron-tabs documentation, it suggests you can use the tabs webview the same as you would a windows web-contents (see here). Unfortunately, either I am missing something or it is a little more complicated.

enter image description here

For the code: I have a main window mainWindow.html

<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
  <div class="etabs-tabs" ></div>
  <div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">

<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;

var tabs = [];

// Create a new tabGroup
let tabGroup = new TabGroup();

// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
    tabs.push(tabGroup.addTab({
        src: 'file://' + __dirname + '/tab.html',
        webviewAttributes: {
            nodeintegration: true
        }
    }));

    // Send test message to tabs... !!!! DOES NOT WORK !!!!
    circuitTabs[i].webview.send('testMessage',i);
}
</script>
</html>

tab.html

<!DOCTYPE html>
<html>
  <head>
  <meta name="viewport" content="width=device-width, initial-scale=1">  
  </head>
  <body>
      <script>
        const electron = require('electron') ;
        const {ipcRenderer} = electron;
        const { remote } = require('electron');

        ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });

      </script>
  </body>
  </html>

main.js

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

let mainWindow = null;

app.on('ready', function () {
    mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
    webPreferences: {
    nodeIntegration: true,
    webviewTag: true
    }
});

mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
    mainWindow.show();
    mainWindow.focus();
});
Jadon Erwin
  • 551
  • 5
  • 25
  • 2
    Maybe this is helpful? https://www.electronjs.org/docs/faq#how-to-share-data-between-web-pages – Michael Rush Jan 13 '21 at 17:00
  • 1
    Yes, I think message ports might be the way to go. Thanks! Let's see if anyone comes up with anything else. – Jadon Erwin Jan 13 '21 at 18:15
  • 1
    I tried to use message ports but the example they give uses i-frames contentWindow to post messages. "iframe.contentWindow.postMessage(message, '*', [channel.port2]);" But, I don't see 'contentWindow' or any equivalent in the electron tabs documentation. – Jadon Erwin Jan 13 '21 at 19:13
  • 2
    Hmm, ok. I can't really be of any help beyond that as I have never tried it. Maybe someone else with experience can chime in. – Michael Rush Jan 13 '21 at 21:44
  • 1
    I got a little further. The electron tabs have a webview element that functions much like the web-contents. Still doesn't work... – Jadon Erwin Jan 14 '21 at 16:10

2 Answers2

3

I figured it out. You can get the webcontentsID from the tab and use ipcRenderer.sendTo function.

For the code: I have a main window mainWindow.html

<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
  <div class="etabs-tabs" ></div>
  <div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">

<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;

var tabs = [];

// Create a new tabGroup
let tabGroup = new TabGroup();

// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
    tabs.push(tabGroup.addTab({
        src: 'file://' + __dirname + '/tab.html',
        webviewAttributes: {
            nodeintegration: true
        }
    }));

    // Send test message to tabs...
    var webContentsID = tabs[i].webview.getWebContentsId();
    ipcRenderer.sendTo(webContentsID,'testMessage');
}
</script>
</html>

tab.html

<!DOCTYPE html>
<html>
  <head>
  <meta name="viewport" content="width=device-width, initial-scale=1">  
  </head>
  <body>
      <script>
        const electron = require('electron') ;
        const {ipcRenderer} = electron;
        const { remote } = require('electron');

        ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });

      </script>
  </body>
  </html>

main.js

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

let mainWindow = null;

app.on('ready', function () {
    mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
    webPreferences: {
    nodeIntegration: true,
    webviewTag: true
    }
});

mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
    mainWindow.show();
    mainWindow.focus();
});
Jadon Erwin
  • 551
  • 5
  • 25
1

Try following this:

<webview>.send(channel, ...args)

channel String
...args any[]

Returns Promise<void>

Send an asynchronous message to renderer process via channel, you can also send arbitrary arguments. The renderer process can handle the message by listening to the channel event with the ipcRenderer module.

See webContents.send for examples.

Look at the docs below for more information.

https://www.electronjs.org/docs/api/webview-tag#webviewsendchannel-args

RobC
  • 22,977
  • 20
  • 73
  • 80
Zaphod
  • 508
  • 4
  • 10