Integrate Angular 7 with ElectronJS - GeeksforGeeks

17 May, 2020
ElectronJS is an Open Source Framework used for building Cross-Platform native desktop applications using web technologies such as HTML, CSS, and JavaScript which are capable of running on Windows, macOS, and Linux operating systems. It combines the Chromium engine and NodeJS into a Single Runtime.
Electron can be combined with several powerful frameworks such as Angular 4+, AngularJS 1.x, React, etc for building complex applications and providing enhanced functionality. Electron at its core is a NodeJS application which can interact with the native OS environment. With NodeJS integration we can access several low-level APIs which otherwise would not be accessible in a Sandbox Browser environment. With Angular integration, we gain access to several advantages and features such as MVC (Model-View-Controller) architecture, modules and custom directives. This tutorial will demonstrate how to integrate Angular 7 with ElectronJS and also access the Electron APIs from within Angular.
We assume you are familiar with the prerequisites as covered in the above-mentioned link. For Electron and Angular to work, node and npm need to be pre-installed in the system.
Note: This tutorial is applicable to work with Angular 5+ versions as well.
Example: Follow the given steps to integrate Angular 7 with Electron.
Step 1: Navigate to an Empty Directory to set up the project, and run the following command,npm install -g @angular/cliTo install the Angular CLI globally. The Angular CLI tool is used to create projects, perform tasks such as testing and deployment, and generate various components of the code. Create a new Angular project by running the following command and provide the project name of your choosing,ng new ang-electronThis command prompts you to provide information about features to include in the project. Choose the appropriate by pressing the Enter key.This will also install the required Angular dependencies and packages into node_modules. Once done, Install Electron using npm and save it as a dev-dependency.npm install electron --save-devAt this point, the Angular application is ready and can be served locally. To serve the application on localhost, run the following commands,cd ang-electron
ng serve
npm install -g @angular/cli
To install the Angular CLI globally. The Angular CLI tool is used to create projects, perform tasks such as testing and deployment, and generate various components of the code. Create a new Angular project by running the following command and provide the project name of your choosing,
ng new ang-electron
This command prompts you to provide information about features to include in the project. Choose the appropriate by pressing the Enter key.
This will also install the required Angular dependencies and packages into node_modules. Once done, Install Electron using npm and save it as a dev-dependency.
npm install electron --save-dev
At this point, the Angular application is ready and can be served locally. To serve the application on localhost, run the following commands,
cd ang-electron
ng serve
Step 2: We will connect both the frameworks via code to launch the Electron application and make it use Angular. As with any Electron project, we need to create an entry point into the application. Create the main.js file in our base project folder. This file is going to be our entry point into the application.main.js:const { app, BrowserWindow } = require('electron')  function createWindow () {  // Create the browser window.  const win = new BrowserWindow({    width: 800,    height: 600,    webPreferences: {      nodeIntegration: true    }  })    // Load the index.html of the app  // From the dist folder which is created   // After running the build command  win.loadFile('dist/ang-electron/index.html')    // Open the DevTools.  win.webContents.openDevTools()}  // This method will be called when Electron has finished// initialization and is ready to create browser windows.// Some APIs can only be used after this event occurs.// This method is equivalent to 'app.on('ready', function())'app.whenReady().then(createWindow)  // Quit when all windows are closed.app.on('window-all-closed', () => {  // On macOS it is common for applications and their   // menu bar to stay active until the user quits   // explicitly with Cmd + Q  if (process.platform !== 'darwin') {    app.quit()  }})  app.on('activate', () => {  // On macOS it's common to re-create a window in the  // app when the dock icon is clicked and there are   // no other windows open.  if (BrowserWindow.getAllWindows().length === 0) {    createWindow()  }})  // In this file, you can include the rest of your app's // specific main process code. You can also put them in // separate files and require them here.For the Boilerplate code of the main.js file, Refer this link. We have modified the code to suit our project needs. Once we run the build command, it is going to create a distribution of our angular project in the dist folder. We are going to refer the index.html file from this folder.
main.js:
const { app, BrowserWindow } = require('electron')  function createWindow () {  // Create the browser window.  const win = new BrowserWindow({    width: 800,    height: 600,    webPreferences: {      nodeIntegration: true    }  })    // Load the index.html of the app  // From the dist folder which is created   // After running the build command  win.loadFile('dist/ang-electron/index.html')    // Open the DevTools.  win.webContents.openDevTools()}  // This method will be called when Electron has finished// initialization and is ready to create browser windows.// Some APIs can only be used after this event occurs.// This method is equivalent to 'app.on('ready', function())'app.whenReady().then(createWindow)  // Quit when all windows are closed.app.on('window-all-closed', () => {  // On macOS it is common for applications and their   // menu bar to stay active until the user quits   // explicitly with Cmd + Q  if (process.platform !== 'darwin') {    app.quit()  }})  app.on('activate', () => {  // On macOS it's common to re-create a window in the  // app when the dock icon is clicked and there are   // no other windows open.  if (BrowserWindow.getAllWindows().length === 0) {    createWindow()  }})  // In this file, you can include the rest of your app's // specific main process code. You can also put them in // separate files and require them here.
For the Boilerplate code of the main.js file, Refer this link. We have modified the code to suit our project needs. Once we run the build command, it is going to create a distribution of our angular project in the dist folder. We are going to refer the index.html file from this folder.
Step 3: A small change is required in the index.html file located in the src folder. Replace the following code.<base href="/">with<'base href="./">This change is important otherwise it won’t be able to find and refer the necessary files required to run the application from dist folder. We also need to make a few changes to the package.json file.package.json:{
  "name": "ang-electron",
  "version": "0.0.0",
  "main": "main.js",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "electron": "ng build && electron ."
  },
// ...
We have specified the main.js file as required by Electron. We have also introduced a new custom electron command in the scripts tag for building and launching the application. The ng build command is used for building the Angular application and deploying the build artifacts. It writes generated build artifacts to the output folder. By default, the output folder is dist/.Output: Once the respective changes are done, we are ready to launch the Electron application. To launch the application, run the command,npm run electron
<base href="/">
with
<'base href="./">
This change is important otherwise it won’t be able to find and refer the necessary files required to run the application from dist folder. We also need to make a few changes to the package.json file.
package.json:
{
  "name": "ang-electron",
  "version": "0.0.0",
  "main": "main.js",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "electron": "ng build && electron ."
  },
// ...

We have specified the main.js file as required by Electron. We have also introduced a new custom electron command in the scripts tag for building and launching the application. The ng build command is used for building the Angular application and deploying the build artifacts. It writes generated build artifacts to the output folder. By default, the output folder is dist/.
Output: Once the respective changes are done, we are ready to launch the Electron application. To launch the application, run the command,
npm run electron
Step 4: At this point, we have successfully integrated Angular with Electron. To use the Electron APIs within Angular, we can follow any of the two approaches:Approach 1: Using an external package to access the Electron APIs. We will use the ngx-electron npm package for this purpose. We can use this package as a simple Angular Service to access Electron APIs. For more detailed Information, Refer this link. To install this package, run the following command:npm install ngx-electron --saveOnce it is successfully installed, we will import it in our app.module.ts file for it to be used throughout the application.app.module.ts:import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';import { NgxElectronModule } from 'ngx-electron';  @NgModule({  declarations: [    AppComponent  ],  imports: [    BrowserModule,    AppRoutingModule,    NgxElectronModule  ],  providers: [],  bootstrap: [AppComponent]})export class AppModule { }For the list of Electron APIs supported by this package, Refer to this https://www.npmjs.com/package/ngx-electron#properties. We will use the Electron shell API from the ngx-electron package.app.component.html:<div style="text-align:center">  <h1>    Welcome to {{ title }}!  </h1></div>  <button (click)="openWindow()">   Click here to Access GeeksForGeeks</button>  <router-outlet></router-outlet>The Click here to Access GeeksForGeeks button does not have any functionality associated with it. To change this, make the following changes to the app.component.ts file.app.component.ts:import { Component } from '@angular/core';import { ElectronService } from 'ngx-electron';  @Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.css']})export class AppComponent {  title = 'ang-electron';    constructor(private electronService: ElectronService) {}    openWindow() {    // Accessing the Shell API from ngx-electron     this.electronService          .shell.openExternal('https://www.geeksforgeeks.org/');  }}The ElectronService exposes all Electron APIs accessible from within the Renderer Process. We will create an Instance of the ElectronService in the constructor through Dependency Injection.Output:Approach 2: By creating an Electron Service component and sharing it across the application for using the Electron APIs. We will generate the Electron Service by running the following CLI command:ng generate service elec --skipTests=trueThe –skipTests=true does not create the spec.ts Test files for new services. This command will generate a new elec.service.ts file within the src/app folder. In this file, we will declare and add all Electron Imports which can then be used throughout the application. We will use the Electron shell API from the Main Process.elec.service.ts:import { Injectable } from '@angular/core';import { shell } from 'electron';  @Injectable({  providedIn: 'root'})export class ElecService {  shell: typeof shell;    constructor() {     this.shell = (<any>window).require("electron").shell;  }}The any keyword is for Type Assertion on the window object. Converting this Object using any indicates that you are no longer bound by the compiler to the default properties of the window object. This is used to prevent compile-time type errors when using Electron modules. If type-casting is ignored on the windows object, we will receive the following error:ERROR in ./node_modules/electron/index.js
Module not found: Error: Can't resolve 'fs'The typeof operator returns the data type of its operand in the form of a string. In this case, the operand is the shell module of Electron. Using this approach gives us access to all Electron APIs throughout the Application. To use this service, add the following to the app.component.ts file.app.component.ts:import { Component } from '@angular/core';import { ElectronService } from 'ngx-electron';import { ElecService } from '../app/elec.service';  @Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.css']})export class AppComponent {  title = 'ang-electron';      // Creating Instances through Dependency Injection  constructor(private electronService: ElectronService,                private elecService: ElecService) {}    openWindow() {    // Accessing the Shell API from ngx-electron     // this.electronService    // .shell.openExternal('https://www.geeksforgeeks.org/');          // Accessing the Shell API from ElecService    this.elecService.shell             .openExternal('https://www.geeksforgeeks.org/');  }}Output:
Approach 1: Using an external package to access the Electron APIs. We will use the ngx-electron npm package for this purpose. We can use this package as a simple Angular Service to access Electron APIs. For more detailed Information, Refer this link. To install this package, run the following command:npm install ngx-electron --saveOnce it is successfully installed, we will import it in our app.module.ts file for it to be used throughout the application.app.module.ts:import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';import { NgxElectronModule } from 'ngx-electron';  @NgModule({  declarations: [    AppComponent  ],  imports: [    BrowserModule,    AppRoutingModule,    NgxElectronModule  ],  providers: [],  bootstrap: [AppComponent]})export class AppModule { }For the list of Electron APIs supported by this package, Refer to this https://www.npmjs.com/package/ngx-electron#properties. We will use the Electron shell API from the ngx-electron package.app.component.html:<div style="text-align:center">  <h1>    Welcome to {{ title }}!  </h1></div>  <button (click)="openWindow()">   Click here to Access GeeksForGeeks</button>  <router-outlet></router-outlet>The Click here to Access GeeksForGeeks button does not have any functionality associated with it. To change this, make the following changes to the app.component.ts file.app.component.ts:import { Component } from '@angular/core';import { ElectronService } from 'ngx-electron';  @Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.css']})export class AppComponent {  title = 'ang-electron';    constructor(private electronService: ElectronService) {}    openWindow() {    // Accessing the Shell API from ngx-electron     this.electronService          .shell.openExternal('https://www.geeksforgeeks.org/');  }}The ElectronService exposes all Electron APIs accessible from within the Renderer Process. We will create an Instance of the ElectronService in the constructor through Dependency Injection.Output:
npm install ngx-electron --save
Once it is successfully installed, we will import it in our app.module.ts file for it to be used throughout the application.
app.module.ts:
import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';import { NgxElectronModule } from 'ngx-electron';  @NgModule({  declarations: [    AppComponent  ],  imports: [    BrowserModule,    AppRoutingModule,    NgxElectronModule  ],  providers: [],  bootstrap: [AppComponent]})export class AppModule { }
For the list of Electron APIs supported by this package, Refer to this https://www.npmjs.com/package/ngx-electron#properties. We will use the Electron shell API from the ngx-electron package.
app.component.html:
<div style="text-align:center">  <h1>    Welcome to {{ title }}!  </h1></div>  <button (click)="openWindow()">   Click here to Access GeeksForGeeks</button>  <router-outlet></router-outlet>
The Click here to Access GeeksForGeeks button does not have any functionality associated with it. To change this, make the following changes to the app.component.ts file.
app.component.ts:
import { Component } from '@angular/core';import { ElectronService } from 'ngx-electron';  @Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.css']})export class AppComponent {  title = 'ang-electron';    constructor(private electronService: ElectronService) {}    openWindow() {    // Accessing the Shell API from ngx-electron     this.electronService          .shell.openExternal('https://www.geeksforgeeks.org/');  }}
The ElectronService exposes all Electron APIs accessible from within the Renderer Process. We will create an Instance of the ElectronService in the constructor through Dependency Injection.
Output:
Approach 2: By creating an Electron Service component and sharing it across the application for using the Electron APIs. We will generate the Electron Service by running the following CLI command:ng generate service elec --skipTests=trueThe –skipTests=true does not create the spec.ts Test files for new services. This command will generate a new elec.service.ts file within the src/app folder. In this file, we will declare and add all Electron Imports which can then be used throughout the application. We will use the Electron shell API from the Main Process.elec.service.ts:import { Injectable } from '@angular/core';import { shell } from 'electron';  @Injectable({  providedIn: 'root'})export class ElecService {  shell: typeof shell;    constructor() {     this.shell = (<any>window).require("electron").shell;  }}The any keyword is for Type Assertion on the window object. Converting this Object using any indicates that you are no longer bound by the compiler to the default properties of the window object. This is used to prevent compile-time type errors when using Electron modules. If type-casting is ignored on the windows object, we will receive the following error:ERROR in ./node_modules/electron/index.js
Module not found: Error: Can't resolve 'fs'The typeof operator returns the data type of its operand in the form of a string. In this case, the operand is the shell module of Electron. Using this approach gives us access to all Electron APIs throughout the Application. To use this service, add the following to the app.component.ts file.app.component.ts:import { Component } from '@angular/core';import { ElectronService } from 'ngx-electron';import { ElecService } from '../app/elec.service';  @Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.css']})export class AppComponent {  title = 'ang-electron';      // Creating Instances through Dependency Injection  constructor(private electronService: ElectronService,                private elecService: ElecService) {}    openWindow() {    // Accessing the Shell API from ngx-electron     // this.electronService    // .shell.openExternal('https://www.geeksforgeeks.org/');          // Accessing the Shell API from ElecService    this.elecService.shell             .openExternal('https://www.geeksforgeeks.org/');  }}Output:
ng generate service elec --skipTests=true
The –skipTests=true does not create the spec.ts Test files for new services. This command will generate a new elec.service.ts file within the src/app folder. In this file, we will declare and add all Electron Imports which can then be used throughout the application. We will use the Electron shell API from the Main Process.
elec.service.ts:
import { Injectable } from '@angular/core';import { shell } from 'electron';  @Injectable({  providedIn: 'root'})export class ElecService {  shell: typeof shell;    constructor() {     this.shell = (<any>window).require("electron").shell;  }}
The any keyword is for Type Assertion on the window object. Converting this Object using any indicates that you are no longer bound by the compiler to the default properties of the window object. This is used to prevent compile-time type errors when using Electron modules. If type-casting is ignored on the windows object, we will receive the following error:
ERROR in ./node_modules/electron/index.js
Module not found: Error: Can't resolve 'fs'
The typeof operator returns the data type of its operand in the form of a string. In this case, the operand is the shell module of Electron. Using this approach gives us access to all Electron APIs throughout the Application. To use this service, add the following to the app.component.ts file.
app.component.ts:
import { Component } from '@angular/core';import { ElectronService } from 'ngx-electron';import { ElecService } from '../app/elec.service';  @Component({  selector: 'app-root',  templateUrl: './app.component.html',  styleUrls: ['./app.component.css']})export class AppComponent {  title = 'ang-electron';      // Creating Instances through Dependency Injection  constructor(private electronService: ElectronService,                private elecService: ElecService) {}    openWindow() {    // Accessing the Shell API from ngx-electron     // this.electronService    // .shell.openExternal('https://www.geeksforgeeks.org/');          // Accessing the Shell API from ElecService    this.elecService.shell             .openExternal('https://www.geeksforgeeks.org/');  }}
Output:
AngularJS-Misc
ElectronJS
AngularJS
Web Technologies
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Angular PrimeNG Dropdown Component
Auth Guards in Angular 9/10/11
Angular PrimeNG Calendar Component
Angular PrimeNG Messages Component
How to bundle an Angular app for production?
Remove elements from a JavaScript Array
Installation of Node.js on Linux
Convert a string to an integer in JavaScript
How to fetch data from an API in ReactJS ?
How to insert spaces/tabs in text using HTML/CSS?