In the article « Angular-ElectronJS – Login API REST jwt », the user’s token was saved in his browser thanks to localStorage.
But here, our goal is to create a cross-platform application: desktop software and web application, so we will adjust our code to meet this goal.
A few steps :
- Creation of a shared storageService that records the information returned by the server:
- either localStorage (in the webapp context)
- or in a json file (in the electronjs context)
- Replacing calls from localStorage .
StorageService
The creation of this service is done with the following Angular command:
ng g service providers/storage
The following files are created :
- angular-electron/src/app/providers/storage.service.ts
- angular-electron/src/app/providers/storage.service.spec.ts
Let’s modify angular-electron/src/app/providers/login.service.ts and import the ElectronService. ElectronService is a service pre-installed in the application. It allows to use the Electronjs API in an Angular application.
import { StorageService } from './storage.service';
import { ElectronService } from './electron.service';
Let’s add some variables before the constructor
public storage: StorageService;
Then let’s add in the manufacturer the ElectronService
constructor(private http: HttpClient, private electron: ElectronService)
Replace all localStorage directives with this.storage in the angular-electron/src/app/providers/login.service.ts file.
Old Code | New code |
---|---|
this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.get('user'))); | this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(this.storage.get('user'))); |
localStorage.setItem('user', JSON.stringify(user)); | this.storage.save('user', user); |
localStorage.removeItem('user'); | this.storage.remove('user'); |
return localStorage.getItem('user'); | return this.storage.get('user'); |
As seen above, the information returned by the server (if the user has been authenticated) will be saved in a .json file (in the electronjs context). We will save the « name of this file » in the configuration file specific to the environment used. Remember that we have chosen the 3 following environments:
- LOCAL
- DEV
- PROD
The LOCAL environment (angular-electron/src/environments/environment.ts) contains the following code
export const AppConfig = {
production: false,
environment: 'LOCAL',
apiUrl: 'http://localhost:3008/api',
configFile: 'token.json'
};
Line 5 of this file contains the .json file name. This file is located at the following location for the windows OS: C:\Users\~\AppData\Roaming\angular-electron\token.json
. The ElectronJs API allows to retrieve the path of this file. Here is an example of this call:
this.electron.remote.app.getPath('userData') + '/' + AppConfig.configFile
The file angular-electron/src/app/providers/storage.service.ts contains the following code
import { Injectable } from '@angular/core';
import {ElectronService} from "./electron.service";
import { AppConfig } from '../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class StorageService {
isElectron: boolean;
confExists: boolean;
electron: ElectronService;
configFile: string;
constructor(electron: ElectronService) {
this.electron = electron;
if (this.electron.isElectron()) {
this.initElectron();
}
}
save(key: string, content: string) {
if (this.isElectron) {
let data = {};
data[key] = content;
this.electron.fs.writeFileSync(this.configFile, JSON.stringify(data));
return;
}
localStorage.setItem(key, JSON.stringify(content));
}
remove(key: string) {
if (this.isElectron) {
// Remove file
this.electron.fs.unlinkSync(this.configFile);
return;
}
localStorage.removeItem(key);
}
get(key: string) {
if (this.isElectron && this.electron.fs.existsSync(this.configFile)) {
let jsonContents = this.electron.fs.readFileSync(this.configFile, "utf8");
jsonContents = JSON.parse(jsonContents);
return JSON.stringify(jsonContents[key]);
}
return localStorage.getItem(key);
}
private initElectron() {
this.isElectron = true;
this.configFile = this.electron.remote.app.getPath('userData') + '/' + AppConfig.configFile;
this.confExists = this.electron.fs.existsSync(this.configFile);
}
}
save
If we look closely at the save method that saves the information returned by the server.
save(key: string, content: string) {
if (this.isElectron) {
let data = {};
data[key] = content;
this.electron.fs.writeFileSync(this.configFile, JSON.stringify(data));
return;
}
localStorage.setItem(key, JSON.stringify(content));
}
Line 2-7 checks if we are in the electronjs context. If so, the information returned by the server (including the token) will be recorded in the json C:\Users\~AppData\Roaming\angular-electron\token.json
.This is possible thanks to the API of electronjs which makes it possible to save a file on the machine. This API simply uses the fs API of nodejs.
this.electron.fs.writeFileSync(this.configFile, JSON.stringify(data));
Line 9 is used in the web application context.
remove
The remove method is slightly different depending on the context:
- electronjs: the file
C:\Users\~AppData\Roaming\angular-electron\token.json
will be deleted. - webapplication: the localstorage containing the user key will be deleted.
get
The get method is similar in both contexts:
- read the information contained in localstorage or in the json file
And there you have it, we’ve achieved our goal. Our cross-platform application is working properly.
Webapplication capture
Capture electronjsapplication
Sources: https://github.com/rabehasy/angular-electron/tree/step3
Translated by Andy A