23

I want to use ipcMain / ipcRenderer on my project to communicate from Angular to Electron and back.

The Electron side is pretty clear:

const
  electron = require('electron'),
  ipcMain = electron.ipcMain,
;

ipcMain.on('asynchronous-message', function(event, arg) {
  console.debug('ipc.async', arg);
  event.sender.send('asynchronous-reply', 'async-pong');
});

ipcMain.on('synchronous-message', function(event, arg) {
  console.debug('ipc.sync', arg);
  event.returnValue = 'sync-pong';
});

But I have no idea how to integrate that Electron module into my Angular 2 app. I use SystemJS as module loader, but I'm a rookie with it.

Any help appreciated. Thanks.

--- Mario

Sommereder
  • 904
  • 4
  • 10
  • 32

6 Answers6

22

There is conflict, because Electron use commonjs module resolving, but your code already compiled with systemjs rules.

Two solutions:

Robust way. Register object require returned:

<script>
    System.set('electron', System.newModule(require('electron')));
</script>

This is the best, because renderer/init.js script loads that module on start. SystemJS have to take it only, not loads.

Alternative way. Use dirty trick with declaration.

Get electron instance inside index.html:

<script>
    var electron = require('electron');
</script>

Declare it inside your typescript file this way:

declare var electron: any;

Use it with freedom )

electron.ipcRenderer.send(...)
DenisKolodin
  • 13,501
  • 3
  • 62
  • 65
  • Thank you. I found that out a while too and created a Gist therefore: https://gist.github.com/MarioHofer/638e63b5695702b793845c3a29328dac But I didn't know the robust way yet. Thanks again. – Sommereder May 20 '16 at 07:37
  • Should the "Robust way" allow me to do this? `import { remote, ipcRenderer } from 'electron';` – objectuser Sep 03 '16 at 13:32
  • 1
    Absolutely! But tsd is necessary too. You can look how I do it here https://github.com/DenisKolodin/tsng2 – DenisKolodin Sep 04 '16 at 15:02
  • 1
    My goodness, I struggled with systemjs with other modules for something like 5 hours to later discover that it's just enough to use System.set. I'm astonished, thank you man, you've made my day. – briosheje Dec 21 '16 at 14:53
  • @DenisKolodin: Hi Denis, I have cloned your repo and try to look into your code and play around with it, I notice there is red underline in plenty of places and one of them is in homepage.component.ts for *import { ipcRenderer } from 'electron';* under electron saying [ts] cannot find electron, also when I tried to make use of dialog API to display error message dialog.showErrorBox(title',content') I got an error at runtime saying cannot find showErrorBox of undefined, I imported dialog inside homepage.component.ts as import { ipcRenderer, dialog } from 'electron'; what went wrong? – Sohail Faruqui Mar 27 '17 at 09:47
15

A recent package called ngx-electron makes this easy. Link to repo and link to article

src/app/app.module.ts

import { NgxElectronModule } from 'ngx-electron';
// other imports 
@NgModule({
  imports: [NgxElectronModule],
  ...
})

src/app/your.component.ts

import { Component, NgZone } from '@angular/core';
import { ElectronService } from 'ngx-electron';

@Component({
  selector: 'app-your',
  templateUrl: 'your.component.html'
})
export class YourComponent {
    message: string;        

    constructor(private _electronService: ElectronService, private _ngZone: NgZone) { 
        this._electronService.ipcRenderer.on('asynchronous-reply', (event, arg) => {
            this._ngZone.run(() => {
                let reply = `Asynchronous message reply: ${arg}`;
                this.message = reply;
            });
        }
    }

    playPingPong() {
        this._electronService.ipcRenderer.send('asynchronous-message', 'ping');
    }
}

Note: NgZone is used because this.message is updated asynchronously outside of Angular’s zone. article

user3587412
  • 1,053
  • 1
  • 12
  • 13
2

But I have no idea how to integrate that Electron module into my Angular 2 app

You would have angular hosted within the UI rendering process in electron. The ipcMain is used to communicate to non rendering child processes.

basarat
  • 261,912
  • 58
  • 460
  • 511
  • I don't get it :-( The docs say [_"messages from the render process (web page) to the main process"_](https://github.com/atom/electron/blob/master/docs/api/ipc-renderer.md). Isn't that what I try to use it for? – Sommereder Mar 30 '16 at 08:20
  • Background: I wanna save a key/value pair on the HDD. User types something into a textfield -> Angular takes it and sends the string to Electron -> Electron writes it into a file. – Sommereder Mar 30 '16 at 08:22
  • Ok, now I know what you mean. Please see title. I want to know how to use ipcRenderer in my Angular2 app to send messages to ipcMain in my Electron app. – Sommereder Mar 30 '16 at 08:44
  • 1
    @MarioHofer, I was never able to get systemjs to load the module correctly. So my solution was to use ExpressJs and just communicate via REST between the renderer and the main process. http://stackoverflow.com/questions/36231588/unable-to-import-electron-modules-from-the-browserwindow-page/36319004#36319004 – TJ Tang Apr 01 '16 at 13:51
1

This should just be a case of requiring the ipcRenderer module in your main html file (electron will provide this for you):

<script>
  var ipc = require('electron').ipcRenderer;
  var response = ipc.sendSync('getSomething');
  console.log(response); // prints 'something'
</script>

and then setting up a handler in your main js file:

const ipcMain = require('electron').ipcMain;
ipcMain.on('getSomething', function(event, arg) {
  event.returnValue = 'something';
});

That's all there should be to it.

KarlPurk
  • 275
  • 1
  • 9
  • 1
    Thanks for the reply, but I know how to use it outside the Angular2 app. The point is, how to get it work inside the Angular2 app (as written in the question). – Sommereder Mar 31 '16 at 07:53
  • I see. The solution is going to depend on how you're building your project. You can take a look at this example which shows how to achieve this with electron, angular 2, babel and systemjs: https://github.com/ThorstenHans/electron-angular-es6 – KarlPurk Mar 31 '16 at 08:39
  • 1
    Thanks for the reply, but this doesn't help me. Everything works fine when using the ipcRenderer in a script tag inside index.html. My problem is to make ipcRenderer useable inside Angular2 app written in TypeScript. – Sommereder Apr 26 '16 at 13:13
1

My solution:

configure a baseUrl in tsconfig.json

at the root of the directory pointed by the baseUrl, create a directory "electron". Inside this directory, a file index.ts:

const electron = (<any>window).require('electron');

export const {BrowserWindowProxy} = electron;
export const {desktopCapturer} = electron;
export const {ipcRenderer} = electron;
export const {remote} = electron;
export const {webFrame} = electron;

(ideally export default [...]require('electron'), but this is not statically analysable...)

now I can have in my renderer process:

import {remote} from 'electron';
console.log(remote);

Hope it makes sense...

with typings enabled:

///<reference path="../../typings/globals/electron/index.d.ts"/>
const electron = (<any>window).require('electron');

export const BrowserWindowProxy = <Electron.BrowserWindowProxy>electron.BrowserWindowProxy;
export const desktopCapturer = <Electron.DesktopCapturer>electron.desktopCapturer;
export const ipcRenderer = <Electron.IpcRenderer>electron.ipcRenderer;
export const remote = <Electron.Remote>electron.remote;
export const webFrame = <Electron.WebFrame>electron.webFrame;

NB: typings I got is from:

{
  "globalDependencies": {
    "electron": "registry:dt/electron#1.4.8+20161220141501"
  }
}
Charles HETIER
  • 1,934
  • 16
  • 28
0

Component.TS

const ipc = require('electron').ipcRenderer;

@Component({
    selector: 'app-my component',.....
})

....

 public testElectronIpc(): void{
        ipc.send('test-alert');
    }

MAIN.JS

// IPC message listeners
ipc.on('test-alert', function (event, arg) {
    console.log('Test alert received from angular component');
})

config

plugins: [ new webpack.ExternalsPlugin('commonjs', [ 'desktop-capturer', 'electron', 'ipc', 'ipc-renderer', 'native-image', 'remote', 'web-frame', 'clipboard', 'crash-reporter', 'screen', 'shell' ]) ],

  • please edit this answer and explain your rationale a little – vencaslac Nov 09 '18 at 12:51
  • Sorry forgot the webpack config that makes first line available ` plugins: [ new webpack.ProvidePlugin({ jQuery: 'jquery', $: 'jquery', jquery: 'jquery' }), new webpack.ExternalsPlugin('commonjs', [ 'desktop-capturer', 'electron', 'ipc', 'ipc-renderer', 'native-image', 'remote', 'web-frame', 'clipboard', 'crash-reporter', 'screen', 'shell' ]) ], ` – quickreplyguest Nov 12 '18 at 09:48