Using Angular with Electron Webpack
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);