Create a Desktop Application with Angular Electron Framework

Introduction

In this article, we will learn how to create or convert an existing application to a Desktop application with Electron framework. The Electron framework allows us to write cross-platform desktop applications using JavaScript, HTML, and CSS. It is built using Node.js and Chromium and is used by Visual Studio Code and other apps.

It supports macOS, Windows, and Linux platforms.

Step 1. Create an Angular application

The first step is to create an angular application using the create application command. Below is the command of the application created for the demo for this article.

ng new desktop-calculator

Step 2. Write an Application Code

We will create a calculator for this article's explanation. Check out the screenshots of the code below.

app.component.html

<div class="container">
  <div class="jumbotron col-sm-4 p-2 m-0 bg-inverse mx-auto" style="border: 1px solid lightgray;border-radius: 2%;">
    <h1 class="text-center">Angular Calculator</h1>
    <label style="font-weight: bolder;">Input</label>
    <div class="input-group input-group-sm col-sm-12 m-0 p-0">
      <div class="col-sm-12 form-control text-lg-right" type="text">{{input}}</div>
    </div>
    <label style="font-weight: bolder;">Result</label>
    <div class="input-group input-group-sm col-sm-12 m-0 p-0">
      <div class="form-control text-sm-right" type="text">{{result}}</div>
    </div>
    <div class="col-sm-12 p-1 m-0">
      <button class="btn btn-info col-sm-6" type="button" (click)="allClear()">C</button>
      <button class="btn btn-warning col-sm-3" type="button" (click)="clear()">x</button>
      <button class="btn btn-secondary col-sm-3" type="button" (click)="pressOperator('/')">/</button>
    </div>
    <div class="col-sm-12 p-1 m-0">
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('7')">7</button>
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('8')">8</button>
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('9')">9</button>
      <button class="btn btn-secondary col-sm-3 p-1" type="button" (click)="pressOperator('*')">X</button>
    </div>
    <div class="col-sm-12 p-1 m-0">
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('4')">4</button>
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('5')">5</button>
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('6')">6</button>
      <button class="btn btn-secondary col-sm-3 p-1" type="button" (click)="pressOperator('-')">-</button>
    </div>
    <div class="col-sm-12 p-1 m-0">
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('1')">1</button>
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('2')">2</button>
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('3')">3</button>
      <button class="btn btn-secondary col-sm-3 p-1" type="button" (click)="pressOperator('+')">+</button>
    </div>
    <div class="col-sm-12 p-1 m-0">
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('.')">.</button>
      <button class="btn btn-outline-secondary col-sm-3 p-1" type="button" (click)="clickNum('0')">0</button>
      <button class="btn btn-success col-sm-6 p-1" type="button" (click)="getAnswer()">=</button>
    </div>
  </div>
</div>

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  input:string = '';
  result:string = '';
  
 
  clickNum(num: string) {
    
    //Do Not Allow . more than once
    if (num==".") {
      if (this.input !="" ) {
 
        const lastNum=this.getLastOperand()
        console.log(lastNum.lastIndexOf("."))
        if (lastNum.lastIndexOf(".") >= 0) return;
      }
    }
 
    //Do Not Allow 0 at beginning. 
    //Javascript will throw Octal literals are not allowed in strict mode.
    if (num=="0") {
      if (this.input=="" ) {
        return;
      }
      const PrevKey = this.input[this.input.length - 1];
      if (PrevKey === '/' || PrevKey === '*' || PrevKey === '-' || PrevKey === '+')  {
          return;
      }
    }
 
    this.input = this.input + num
    this.calcAnswer();
  }
 
 
  getLastOperand() {
    let pos:number;
    console.log(this.input)
    pos=this.input.toString().lastIndexOf("+")
    if (this.input.toString().lastIndexOf("-") > pos) pos=this.input.lastIndexOf("-")
    if (this.input.toString().lastIndexOf("*") > pos) pos=this.input.lastIndexOf("*")
    if (this.input.toString().lastIndexOf("/") > pos) pos=this.input.lastIndexOf("/")
    console.log('Last '+this.input.substr(pos+1))
    return this.input.substr(pos+1)
  }
 
 
  pressOperator(op: string) {
 
    //Do not allow operators more than once
    const lastKey = this.input[this.input.length - 1];
    if (lastKey === '/' || lastKey === '*' || lastKey === '-' || lastKey === '+')  {
      return;
    }
   
    this.input = this.input + op
    this.calcAnswer();
  }
 
 
  clear() {
    if (this.input !="" ) {
      this.input=this.input.substr(0, this.input.length-1)
    }
  }
 
  allClear() {
    this.result = '';
    this.input = '';
  }
 
  calcAnswer() {
    let formula = this.input;
 
    let lastKey = formula[formula.length - 1];
 
    if (lastKey === '.')  {
      formula=formula.substr(0,formula.length - 1);
    }
 
    lastKey = formula[formula.length - 1];
 
    if (lastKey === '/' || lastKey === '*' || lastKey === '-' || lastKey === '+' || lastKey === '.')  {
      formula=formula.substr(0,formula.length - 1);
    }
 
    console.log("Formula " +formula);
    this.result = eval(formula);
  }
 
  getAnswer() {
    this.calcAnswer();
    this.input = this.result;
    if (this.input=="0") this.input="";
  }
 
}

Run the angular application using the below command.

ng serve –o

Angular calculator

The application works fine with the browser. Now let’s convert to the desktop application.

Step 3. Install Electron to Angular application

Command to install the Electron

npm install electron

npm install electron

Check the package.json file that reference is added for the electron.

{
  "name": "desktop-calculator",
  "version": "0.0.0",
  "main": "app.js",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "electron": "electron .",
    "electron:build": "ng build && electron ."
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^15.2.0",
    "@angular/common": "^15.2.0",
    "@angular/compiler": "^15.2.0",
    "@angular/core": "^15.2.0",
    "@angular/forms": "^15.2.0",
    "@angular/platform-browser": "^15.2.0",
    "@angular/platform-browser-dynamic": "^15.2.0",
    "@angular/router": "^15.2.0",
    "@ng-bootstrap/ng-bootstrap": "^14.2.0",
    "@popperjs/core": "^2.11.6",
    "bootstrap": "^5.3.3",
    "electron": "^32.1.0",
    "electron-packager": "^17.1.2",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.12.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^15.2.2",
    "@angular/cli": "~15.2.2",
    "@angular/compiler-cli": "^15.2.0",
    "@angular/localize": "^15.2.0",
    "@types/jasmine": "~4.3.0",
    "jasmine-core": "~4.5.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.0.0",
    "typescript": "~4.9.4"
  }
}

Step 4. Add JavaScript file reference to the electron

Now add a javascript app.js file at an application root level.

app.js

Add the code like the below screen print to the app.js file and set the path for the index.html file generated in the dist folder of the application.

const { app, BrowserWindow } = require('electron/main')
const path = require('node:path')

function createWindow () {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  win.loadFile('dist/desktop-calculator/index.html')
}

app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

Step 5. Set the Package.json file

Add/update the “main” key in the config file at the root level. Set the app.js file name as a value to the main key added.

To run the application with the electron framework we need to add a command to the script key of the package.json file. Check out the below screen print for the reference.

Set the Package.json file 

Step 6. Run the Application as a Desktop

Once all configuration is done it’s all set to run from the command line or visual code terminal using the below command.

npm run electron:build

After successfully building and running it will open the application as a desktop application. Below is the screen print of the demo application.

Angular Calculator

It’s all set and we can see our angular application running as a desktop application. So we can say we have installed the angular electron properly. But this is not the exact solution we want. We need an executable file that we can execute directly without execute any command. So let’s check out how we can generate exe.

Step 7. Install Electron Packager

Open the application as a desktop application we need an electron packager. Now close the application and install the packager using the below command.

npm install electron-packager

Electron package

Step 8. Generate the Application Package

To generate the application package which we can execute directly from the windows use the below command.

npx electron-packager ./ calculatorApp –plateform=win32 –overwrite

Explanation of command

  • Npx is used because we have not installed the packager at the global level. To execute at the application level we need to use npx.
  • Electron-package is the command to generate the package
  • ./ is the directory info where to generate package files.
  • The platform is used to set select operating systems like Windows, Mac, or Linux. Here in this demo, we are using Windows so we have selected win32.
  • Overwrite is just a command if the package is already generated previously and we want to overwrite it with new changes.

Electron package- calculator app

Checkout the directory it should generate a folder with the application name you have mentioned in the command.

File structure

Step 9. Run the exe from the folder

Now we are all set and ready with the desktop application version of the angular application. Let’s go to the directory of the generated app package and try to run the exe and check the application result.

File structure

Angular_Desktop_App_Execution_Demo

Conclusion

In this article, we have learned about the Electron framework. We can use it to create a desktop application of an existing or brand-new application. It supports a cross-platform, we can create desktop apps for Windows, Mac, and Linux from source angular applications.


Similar Articles