If you can build a website, you can build a desktop app.
That’s true if you know how to write Javascript code, you can build a desktop application that looks and behaves like the native one. not just that, you also can bring the power of Angular into the world of desktop apps to make your IU look stunning 😍, enough talking, go get yourself a cup of coffee and let’s get started.
What are we going to do?!!
We’re going to build a basic desktop application using Electron and Angular.
Before we start I expect that you have some basic knowledge of NodeJs and Angular.
Set up Angular
If you don’t have Angular cli installed already, run the following command to install it.
$ npm install -g @angular/cli
Now let’s start a new Angular application.
$ ng new angular-electron
It’ll ask you about the styles compiler you want to use and if you want to use the Angular router and so on, this configuration doesn’t matter at all select whatever you want.
You can see your application now in action by running…
$ cd angular-electron
$ ng serve
Then open your browser at http://localhost:4200/, anyway that’s not the funniest part, let’s move forward.
We need to modify the index.html file at the src folder of our project, add a period to the base tag, so our app can find the static files, don’t skip this step it’s very important.
<base href="./">
Set up Electron
Now we going to add Electron to our application.
$ npm install --save-dev electron
And we’ll also need some dependencies.
$ npm install --save-dev app-root-path
Now let’s create a new folder for our Electron application.
$ mkdir bin && cd bin && touch main.ts
As you can see we created bin folder with a main.ts
file in it, and the reason we created the main file with ts
extension and not js
is that we already using Typescript for the Angular application, so why not use Typescript for the entire project?!!
Now let’s add some code to our main.ts
file (lastly we writing some code 😅)
import { app, BrowserWindow } from 'electron';
import { resolve } from 'app-root-path';
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win: BrowserWindow;
function createWindow () {
// Create the browser window.
win = new BrowserWindow({
width: 800,
height: 600
});
// Load the angular app.
// Make sure that this path targets the index.html of the
// angular application (the distribution).
win.loadFile(resolve('dist/angular-electron/index.html'));
// Emitted when the window is closed.
win.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi-windows, this is the time
// when you should delete the corresponding element.
win = null;
});
}
// This method will be called when the Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', 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 (win === null) {
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.
The code above is exactly the same as mentioned at the official website but in Typescript syntax, also note that the win.loadFile
function linking to the entry file of the Angular application “we didn’t build it yet”.
Okay, we need to check if what we are doing is even working, right!!
Let’s add a script to our package.json
file so we can build and run this application.
"main" : "bin/main.js",
"scripts": {
...
“electron”: “tsc bin/main.ts && ng build && electron bin/main.js”
}
And now let’s see it in action.
$ npm run electron
For now, you should see the application up and running with the angular logo in it, so far so good 😉.
Okay, now we have our application running, but who could we use the Electron API within the Angular application itself?!!
Don’t panic it’s as easy as running…
$ npm install --save-dev ngx-electron
Access Electron API from within the Angular application.
We just installed ngx-electron which going to make our life a lot easier, so let’s see how to use it.
We need to import this module like any other module we used to use with Angular inside app.module.ts
file.
import { NgxElectronModule } from 'ngx-electron';
@NgModule({
imports: [
...
NgxElectronModule
]
})
export class AppModule {}
That’s it now we can use it in our components like…
import { ElectronService } from 'ngx-electron';
export class AppComponent {
constructor(private _electronService: ElectronService) {
// now we have access to electron api through this service
}
}
Let’s see if we really have access to Electron API.
Replace the content of your app.component.ts
file with the following.
import { Component } from '@angular/core';
import { ElectronService } from 'ngx-electron';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'angular-electron';
versions = { node: '', chrome: '', electron: '' };
constructor(private _electronService: ElectronService) {
// Make sure that app is being executed inside of electron.
if (this._electronService.isElectronApp) {
// We have access to node process.
this.versions.node = this._electronService.process.versions.node;
this.versions.chrome = this._electronService.process.versions.chrome;
this.versions.electron = this._electronService.process.versions.electron;
}
}
}
And replace the content of the app.component.html
file with the following
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>Welcome to {{ title }}!</h1>
<ul>
<li>Node version {{ versions.node }}</li>
<li>Chrome version {{ versions.chrome }}</li>
<li>Electron version {{ versions.electron }}</li>
</ul>
</div>
So, what do you think? don’t think a lot let’s see it in action 😅.
$ npm run electron
For now, you should see the application up and running with the versions of code, chrome, and electron we using, hmmm we did it 😉.
Conclusion
Building desktop apps isn’t that hard, and we can use some powerful tools like node, angular, electron, and typescript to do awesome work, and you know what? even if you're web developer it’s not bad to try some new techs in your free time, I'm pretty sure that you going to learn something new from trying new techs 😉.
I may add a new post to write some unit tests for our application if I have free time.
And if you stuck you can always refer to the Github repo of this application.
This post was originally published at Medium
Top comments (21)
Hi Ahmed! When running "npm run electron" for the first time, it shows a blank page. Have you ever had this problem? Thanks for the post!
Hi Borja, the blank page means that the angular application has some error usually because you forget to end some tag, so can you check the DevTools console?!!
To open it select toggle DevTools from the toolbar or add win.webContents.openDevTools(); after win.loadFile(resolve('build/index.html'));
This is what it shows.
thepracticaldev.s3.amazonaws.com/i...
It seems that your win.loadFile(); has a wrong path make sure that the path at this function located the index.html of the built application usually it lives at
dist/your-app-name/index.html
It seems that I forgot the
your-app-name
part at my post I'll update it.Here is my project structure: thepracticaldev.s3.amazonaws.com/i...
And here my main.ts file:
thepracticaldev.s3.amazonaws.com/i...
Okay,
dist/control-panel/index.html
looks good but the output of thetsc
"the main.ts file" where is it?!! I see that you haveout-tsc
directory if themain.js
file is at this dir update youpackage.json
replacedist/bin/main.js
withdist/tsc-out/main.js
.Can you upload your project to GitHub? or you can refer to the repo I have mentioned at the post so you can find the right structure.
Tell me if you still have an issue.
There it is: github.com/borjalo/control-panel
As I mentioned it was the output of
tsc
fixed hereThank you very much!
Let me know if you have any issues
Hi Ahmed,
First of all thank you for the example you put together. Nevertheless i get an compile error, when starting it with "npm run electron" right after the step where you modify the package.json
You add
"main" : "bin/main.js",
"scripts": {
...
“electron”: “tsc bin/main.ts && ng build && electron bin/main.js”
}
to the package.json, and that leads to a compile error:
...
error TS2583: Cannot find name 'Map'
After some digging I found the solution [1] to the problem.
If you add the following script:
“electron”: “tsc bin/main.ts && ng build && electron bin/main.js”
this leads to the fact, that the type-script compiler does not as intented. When calling tsc with a file-name parameter, then it ignores the tsconfig.json, therefore we have to add the following to the script:
“electron”: “tsc && ng build && electron bin/main.js”
et voila, problems solved.
Thank you for the post!
[1] for reference: github.com/Microsoft/TypeScript/is...
Ftf, sorry for not replying earlier, thanks for noting out this issue, I'll review my post and update it if needed, also you're very welcome to contribute for the Github repo if you see any fixes or updates.
Hi Ahmed!
Thanks for the post.
I followed along until the part using ngx-electron but when I get to that part, I don't have access to process, or any other property on _electronService (they are all null). However, this._electronService.isElectronApp returns true so the app is running inside Electron.
On checking your Github repo I see it was updated recently and looks to no longer use ngx-electron? In addition the scripts are different in package.json, and the bin folder has disappeared with bin/main.ts being replaced by main.js.
Just wondering the reason for the changes - is it a problem with ngx-electron?
Hi Nick!
It's good to hear that the issue has been solved 👍.
I updated the repo
cause I was using Angular 7 and Electron 4 while writing this post and those are outdated now, and no, there's no problem with
ngx-electron` but we don't need it anymore with the latest versions of Angular and Electron.I'm going to update this post very soon in order to be compatible with the latest versions of Angular and Electron, and I'm going to make the demo app actually does something instead of being a blank app, so, STAY TUNED 🚀.
Awesome, thanks Ahmed! Looking forward to the new demo app 👀 👍
EDIT: I solved the issue by updating my main.ts file to include webPreferences, as follows:
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
})
Hi Ahmed! thank you for the example. Can I ask on how to self-update(versioning) the desktop app using this angular-electron?
Hi pi, sorry for the late reply, and very good question 👍 I'll make a new post for that and let you know when it's published.