Select Page

Intro

I recently had the opportunity to conduct an Electron workshop at ng-conf 2016 and I wanted to share my slides and project, as well as a few screencasts (since I could not help myself). For the uninitiated, Electron is a runtime that allows you to package your HTML web applications into native desktop applications. Native programming is something that I looooooooooooooooove… trying to learn how to like even a little (Brian Regan nod). Thankfully, Electron makes it ridiculously easy!

Our sample application is an Instagram style application build in Electron and after investing a half of a million dollars in market research, we are confident that the brand Electrogram is going to take off. Download the repo and follow along with the screencasts to get your learn on!

Code

The Slides

Hello Electron!

 

Let us take a moment to do a quick overview of how Electron works and what a basic app looks like. Electron has two main components: the main process and the renderer process. The main process is what runs when package.json is executed and is responsible for creating the BrowserWindow object which our renderer process runs in.

There are three main files that compose an Electron application. The index.html file contains normal HTML markup. By itself, it would simply serve up in a browser just like any other website. This is also where you will include all your CSS and JS, just like any other web app.

The main.js contains the logic that bootstraps the Electron app. It is basically just a node script that can open windows and tabs, and provide much more. Finally, package.json contains all of the necessary NPM packages for the app. The important piece of this file is the “main” attribute: this is what the packager uses when you want to build the app for native operating systems.

Getting started

To use electron globally, run npm i -g electron-prebuilt. When you want to run an electron application, run electron .js (generally main.js). This will start the main process in main.js.

Adding Angular 2

 

Structure

We generally like to include all Angular code in a sub-directory, usually src or app[. Any files that deal strictly with Electron we keep in the root folder; unless, of course, there are a bunch of files, in which case it would make sense to put supporting files in a sub-directory.

Integration

You can reference your javascript files in index.html, just like any other web application or site, and then run electron. Since new windows are just web pages served using Chromium and built off of HTML files, your Angular app will be bootstrapped as soon as the window is opened and will behave identically to a web page.

Webpack

Since we are just serving HTML and CSS, Webpack will still work as before. The main difference for us was that we added a target attribute to the webpack config and set it to electron-renderer. This gave us the ability to import node and electron modules directly.

Typings

Since the Angular app is in typescript and we want to integrate some Electron modules, we had to install the electron typings to use electron packages in our code.

Notifications

 

HTML5 provides an amazing notification API that is incredibly simple to use. Electron connects the HTML5 notification API with the native notification API; the result is that you write HTML5 notifications in your app, but the end user sees native notifications. To create a notification in your app, follow this format:

let myNotification = new Notification('Image Saved', {
 body: fileName
});

Here we are creating an instance of the Notification class, passing in a title and a configuration object, and that is it. Now when an image is saved, we get a native notification alerting us.

Inter-Process Communication (IPC)

 

Inter-process communication (IPC) is handled by two different modules: ipcMain in the main process and ipcRenderer in the renderer process. One scenario for IPC is calling a “Save As” task from the main menu of the app. The menu was built in the main process, but files are saved in a renderer process so we need a way to communicate.

When someone clicks the “Save As” menu item, we want to broadcast a save-file message from mainWindow.webcontents. Any listener attached to the save-file channel will be notified and its callback invoked.

// main.js (main process)
let mainWindow = new BrowserWindow({width: 800, height: 600});
...
fileMenu.submenu
  .find(item => item.label === 'Save As...')
  .click = () => mainWindow.webContents.send('save-file')

We use ipcRenderer to listen for the save-file event in the renderer process and call the class method save. This is an easy way to get communication from the main process to the renderer processes.

// app.ts (renderer process)
import {ipcRenderer} from 'electron';
constructor() {
  ipcRenderer.on('save-file', this.save.bind(this));
}

Packaging Electron

 

It used to be that we had to worry about packaging for each OS ourselves and it involved a lot of moving pieces. Fortunately, a package has been written that abstracts most of the logic away and allows you to run one script and build for most any OS.

First, you will need the electron-packager npm module via npm i -g electron-packager.

To use this package, just run electron-packager on the command line. The OS, architecture, and other options are set via flags:

electron-packager . <appName>
--platform=<platform>
--arch=<architecture>
--out=<outputDirectory>
--overwrite
--icon=path/to/custom/icon
--asar

For the case of our particular app it was:

electron-packager . Electrogram --platform=darwin --arch=x64 --out=releases/ --overwrite --icon=src/assets/images/electrogram  --asar

What we essentially accomplished was taking the current working directory, packaging it with the name “Electrogram”, targeting OSX on the x64 architecture, outputting the result to the “releases” directory, overwriting the OSX package in “releases” if it exists already, using the specified icon, and archiving the source code.

Here is a list of the options we can use:

  • platform: the OS you are targeting… could be one or more of darwin, linux, mas, win32 (comma-delimited if multiple)
  • arch: the architecture you are targeting… could be one of all, ia32, x64
  • out: where the resulting files will go
  • overwrite: whether or not to replace the directory for a platform if it has already been created
  • icon: a custom icon to use for the app… see “custom icon” section below
  • asar: packages the source code within your app into an archive

Custom Icon

We can also specify a custom icon for our app when we package it for distribution. The first thing we need to do is to create an icon for the correct format for our OS. For Mac, it is .icons and for Windows and Linux it is .png or .ico. Note, you do not need to specify the extension for the icon which allows us to create an icon in different formats and let our packager sort it out for us.

Resources

Code

Electron Documentation
electron-packager Documentation
Building a desktop application with Electron Great article!