Oggi, le applicazioni web che utilizziamo quotidianamente sono state sviluppate utilizzando framework e tecnologie che permettono agli sviluppatori di essere efficienti e veloci.
Al contrario, sviluppare applicazioni desktop rimane piuttosto complicato e macchinoso.
Per sviluppare applicazioni desktop, infatti, non c’è un unico stack tecnologico a cui fare riferimento: sono diffusi decine di linguaggi di programmazione (Java, C++, Python, …) e decine di framework e librerie per la costruzione di interfacce grafiche (Qt, Gtk, Swing, JavaFX, …).
Spesso queste librerie, poi, possono essere anche molto complesse da utilizzare (come faccio un layout fluido?), hanno scarsa documentazione e pochi esempi di codice…. Senza considerare che alcune non sono nemmeno cross-platform!
Perchè sviluppare applicazioni desktop deve essere così complicato? Come sarebbe se potessimo, in qualche modo, utilizzare le tecnologie web per creare app desktop con la stessa facilità?
Per rispondere a queste domande nasce Electron: un framework per la creazione di applicazioni native con tecnologie web come JavaScript, HTML e CSS, sviluppato da GitHub.
Ogni applicazione Electron è compatibile con tutti i sistemi operativi (Mac, Windows e Linux) e si integra perfettamente con i rispettivi menu nativi e sistemi di notifiche.
Già parecchie aziende hanno capito il potenziale di Electron ed hanno iniziato a sfruttare questa tecnologia, le app Electron sono tantissime ed insospettabili: Whatsapp, Skype, Slack, l’editor di testo Atom, Microsoft Visual Studio Code solo per citarne alcune.
In questo articolo vedremo come impostare un’applicazione Electron da zero con l’ultima versione di Angular e come preparare la nostra app per la distribuzione.
Creare una classica applicazione Angular
Per prima cosa creiamo una nuova applicazione Angular utilizzando l’Angular CLI:
npm install -g @angular/cli ng new angular-electron cd angular-electron
Modificare il file index.html
La pagina index.html generata di default punta il base href a /, questo ci creerà problemi con Electron e va cambiato.
E’ sufficiente aggiungere un punto prima dello slash nel file src/index.html.
<base href="./">
Installare Electron
Procediamo ad installare Electron utilizzando npm.
npm install electron --save-dev
Creare il file main.js
Ora che abbiamo installato Electron come dipendenza di sviluppo, possiamo creare il file main.js che sarà l’entrypoint vero e proprio della nostra applicazione.
const {app, BrowserWindow} = require('electron'); // E' necessario mantenere un riferimento globale all'oggetto // della finestra principale dell'app // altrimenti la finestra verrà chiusa automaticamente quando l'oggetto // verrà eliminato dal garbage collector di Javascript let mainWindow; function createWindow () { // Crea la finestra mainWindow = new BrowserWindow({width: 800, height: 600}); // e carica il file index.html presente nella directory 'dist' // prodotto dalla build di Angular mainWindow.loadFile(`file://${__dirname}/dist/angular-electron/index.html`); // Callback da invocare quando la finestra viene chiusa mainWindow.on('closed', function () { // L'oggetto finestra non ci servè più, dereferenziamolo mainWindow = null; }); } // Crea la finestra quando Electron è pronto app.on('ready', createWindow); // Callback da invocare quando tutte le finestre sono chiuse app.on('window-all-closed', function () { // In macOS è comune che le app rimangano attive // fino a che l'utente non le chiude esplicitamente con Cmd + Q if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', function () { // In macOS è comune che la finestra di un applicazione venga ricreata // quando viene cliccata l'icona nel Dock e non ci sono altre finestre aperte if (mainWindow === null) { createWindow(); } });
Creare il comando di build ed esecuzione in modalità produzione
Aggiungiamo al file package.json alcuni comandi per fare la build e lanciare la nostra applicazione.
{ "name": "angular-electron", "version": "0.0.0", "main": "main.js", // <-- Aggiungi questa riga "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", "electron-build": "ng build --prod && electron ." // <-- Aggiungi questa riga }, ...
Ora, lanciare la build di Angular ed eseguire la nostra app sarà semplice come digitare
npm run electron-build
Se hai eseguito tutti i passi precedenti correttamente, dopo aver lanciato questo comando, dovresti vedere la finestra della nostra app con all’interno la pagina di test con il logo di Angular.
Ambiente di sviluppo con hot code reload e strumenti per sviluppatori di Chrome
La nostra app è già pronta per essere distribuita (vedi sezione seguente) e non necessita di altro per funzionare.
E’ utile, però, configurarla per lo sviluppo in modo tale che le modifiche al codice Angular vengano caricate immediatamente, senza dover rieffettuare build, in una finestra dell’app che terremo sempre aperta.
Per fare ciò, prevederemo la possibilità di lanciare la nostra app in modalità sviluppo, impostando una variabile d’ambiente ENV con il valore DEV.
La finestra dell’app, se ENV=DEV, punterà all’indirizzo localhost:4200, dove sarà in ascolto il server web di sviluppo di Angular lanciato con il comando ng serve .
Siccome ng serve prima di mettersi in ascolto su localhost:4200 effettua alcune operazioni preliminari, potrebbe non essere subito raggiungibile.
E’ per questo motivo che, prima di far puntare la finestra Electron a localhost:4200, ci assicuriamo che ng serve sia in ascolto, altrimenti attendiamo e ritentiamo successivamente.
Modificare il file main.js
Nel file main.js al posto di
mainWindow.loadFile(`file://${__dirname}/dist/angular-electron/index.html`);
andiamo ad inserire
if (process.env.ENV == 'DEV') { // Mi collego all'endpoint ogni retryInterval millisecondi // fino a che non ricevo correttamente la pagina const request = require('request'); const devLoadUrl = function(url, retryInterval) { request(url, function (error, response, body) { if (!error && response.statusCode == 200) { // Carica la pagina in una finestra Electron win.loadURL(url); // Apri gli strumenti per sviluppatori win.webContents.openDevTools(); } else { setTimeout(function() { devLoadUrl(url); }, retryInterval); } }); }; devLoadUrl('http://localhost:4200', 1000); } else { // Se la variabile d'ambiente ENV non è settata a DEV // procedo a caricare il file index.html // prodotto dalla build di Angular win.loadURL(`file://${__dirname}/dist/angular-electron/index.html`); }
Creare il comando per lanciare l’app in modalità sviluppo
Aggiungiamo al file package.json il comando per lanciare la nostra applicazione in modalità sviluppo.
{ "name": "angular-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-start": "ENV=DEV electron . | ng serve", // <-- Aggiungi questa riga "electron-build": "ng build --prod && electron ." }, ...
Ora, per lanciare l’applicazione in modalità sviluppo, sarà sufficiente lanciare
npm run electron-start
Preparare l’applicazione per la distribuzione
Quando avremo terminato la nostra app, arriverà il momento di condividerla con il mondo.
Grazie ad Electron, la creazione di pacchetti per i vari sistemi operativi sarà davvero semplice.
Installiamo il tool electron-packager con npm
npm install electron-packager -g
Per creare gli eseguibili della nostra app basterà lanciare electron-packager con il parametro platform opportuno.
Il tool electron-packager, oltre al parametro platform, ha molte altre configurazioni possibili, rimando alla documentazione ufficiale per il dettaglio.
Eseguibile macOS:
electron-packager . --platform=darwin
Eseguibile Linux:
electron-packager . --platform=linux
Eseguibile Windows:
electron-packager . --platform=win32
Per poter generare un eseguibile Windows utilizzando electron-packager da sistemi Linux o macOS è necessario avere installato WineHQ.