Using Angular with Electron Webpack

Darren Mothersele
Kendraio
Published in
4 min readOct 20, 2017
Photo by Clément H on Unsplash

Simple setup for getting Angular 4 working with Electron Webpack, with template loading, and an optional CSS library (Bulma). I’m using Yarn, but you can probably use npm. The Electron Builder dependency recommends Yarn.

1. Basic Setup

First start with an empty directory and initialise the Angular dependencies:

yarn add @angular/common @angular/compiler @angular/core \
@angular/forms @angular/http @angular/platform-browser \
@angular/platform-browser-dynamic @angular/router \
core-js rxjs source-map-support zone.js

Then add the --dev dependencies for Electron Webpack (also include Typescript, and the Electron Webpack Typescript add-on):

yarn add --dev electron-webpack electron-webpack-ts electron \
webpack typescript @types/node

To use Electron Webpack, you need to create two entry points. The main thread, and the renderer thread. For the main entry point, I’ve converted the example from the Electron Webpack starter project to use Typescript. Create this file as src/main/index.ts

import { app, BrowserWindow } from 'electron'

const
isDevelopment = process.env.NODE_ENV !== 'production';

let mainWindow : BrowserWindow;

function createMainWindow() {
const window = new BrowserWindow();

// Set url for `win`
// points to `webpack-dev-server` in development
// points to `index.html` in production
const url = isDevelopment
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
: `file://${__dirname}/index.html`;

if (isDevelopment) {
window.webContents.openDevTools();
}

window.loadURL(url);

window.on('closed', () => {
mainWindow = null;
});

window.webContents.on('devtools-opened', () => {
window.focus();
setImmediate(() => {
window.focus();
})
});

return window;
}

app.on('window-all-closed', () => {
// On macOS it is common for applications to stay open
// until the user explicitly quits
if (process.platform !== 'darwin') app.quit()
});

app.on('activate', () => {
// On macOS it is common to re-create a window
// even after all windows have been closed
if (mainWindow === null) mainWindow = createMainWindow();
});

// Create main BrowserWindow when electron is ready
app.on('ready', () => {
mainWindow = createMainWindow();
});

For the renderer I’ve combined Angular’s polyfill.ts , vendor.ts and main.ts to create src/renderer/index.ts

// Polyfills
import "core-js/es7/reflect";
require("zone.js/dist/zone");

// Vendor
import "@angular/platform-browser";
import "@angular/platform-browser-dynamic";
import "@angular/core";
import "@angular/common";
import "@angular/http";
import "@angular/router";
import "rxjs";

// main

import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { enableProdMode } from "@angular/core";
import { AppModule } from "./app/app.module";

if(process.env.NODE_ENV === "production") {
enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule);

Electron Webpack creates the index HTML. It includes an element with the ID #app which is used as the selector in the main src/renderer/app/app.component.ts

import {Component, ViewEncapsulation} from "@angular/core";@Component({
selector: '#app',
template: '<h1>App works!</h1>',
encapsulation: ViewEncapsulation.None
})
export class AppComponent {}

The AppModule which loads Angular and bootstraps the AppComponent is defined in src/renderer/app/app.module.ts with:

import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {FormsModule} from "@angular/forms";
import {HttpModule} from "@angular/http";
import {AppComponent} from "./app.component";

@NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule
],
declarations: [
AppComponent
],
bootstrap: [
AppComponent
]
})
export class AppModule {}

In the root directory of the project create a tsconfig.json to configure Typescript for compiling Angular:

{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"./node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
},
"include": [
"./**/*"
]
}

Add a scripts section to your package.json with the command to run Electron Webpack in development mode:

"scripts": {
"dev": "electron-webpack dev"
}

You are now ready to start your minimal app using the following command:

yarn dev

2. Adding Support for Angular Templates

The minimal example above uses inline Angular templates. In order to get Angular template files working you need to add the correct webpack loader and customise the webpack config.

First add the required dependencies to the project:

yarn add --dev angular2-template-loader

Create a custom webpack config file called webpack-renderer-additions.js with the following content:

module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: "ts-loader"
},
{
loader: "angular2-template-loader"
}
]
}
]
}
};

And tell Electron Webpack to load this config by adding the following into the project’s package.json

"electronWebpack": {
"renderer": {
"webpackConfig": "webpack.renderer.additions.js"
}
}

You can now add a template for the App component, create src/renderer/app/app.component.html with the following content:

<h1 class="title">App Works!</h1>

And update src/renderer/app/app.component.ts to remove the inline template and reference this template file:

import {Component, ViewEncapsulation} from "@angular/core";

@Component({
selector: '#app',
templateUrl: './app.component.html',
encapsulation: ViewEncapsulation.None
})
export class AppComponent {}

3. Using a CSS Library (Bulma)

I’m adding Bulma to speed up prototyping the front end markup by having a minimal CSS library that is easily customisable with SASS. To get this working, add the library to the project:

yarn add bulma

and the --dev dependencies to work with SASS in Electron Webpack:

yarn add --dev sass-loader node-sass

Create a basic styles file in src/renderer/app/styles.scss that references the Bulma, for example:

// optionally configure variables to customise Bulma
// before importing...
@import "~bulma"
;
// optionally add extra SASS after importing Bulma

Import the SASS into your app, by updating src/renderer/app/app.component.ts to:

import {Component, ViewEncapsulation} from "@angular/core";

import "./styles.scss";

@Component({
selector: '#app',
templateUrl: './app.component.html',
encapsulation: ViewEncapsulation.None
})
export class AppComponent {}

Re-run Electron Webpack with the dev server by running the command:

yarn dev

That’s it! A simple, minimal example of using Angular 4 with Electron Webpack. Example code is available on GitHub.

Bonus:

If you want to use the Angular Router then you need to inject a <base /> tag into the index.html that is generated by Electron Webpack. You can do this by adding the following code to src/renderer/index.ts just before the bootstrapModule(AppModule) line:

const base = document.createElement('base');
base.href = './';
document.head.appendChild(base);

--

--