Getting started with Electron.js and Angular

Do you know about Electron API (a GitHub repository that helps web developers build standalone cross-platform apps)? Electron can turn any web app into a Desktop Application for all three major OSes (Windows, Mac, and Linux).
If you are an Angular developer who wants to build a Desktop app with a MySQL database, you came to the right place!
The directory structure should look like this:
AngularElectronMySQL/
├── Electron/
├── WebApp/ (Angular app)
└── Setup/
Start by initializing your Angular project using angular-cli. Here, I am assuming that you have already completed and built your Angular project. Add a data service for fetching data from MySQL and subscribe to the MySQL server (for our case: http://localhost:1024). After that, copy the built files into the Electron folder. I used an npm script for this:
// copy.js
const fs = require('fs-extra')
var del = require("delete");
//delete js
console.log("Deleting Old JS");
del.sync('../electron/main.*.js', {
force: true
});
console.log("Deleted");
// copy files
try {
console.log('Copy started');
fs.copySync('./dist/', '../electron/');
} catch (ex) {
console.log('Error : ' + ex);
} finally {
console.log('Copied');
}
Now, for Electron create a main.js file inside the Electron folder. Write down this code:
// main.js
const setupEvents = require('./installers/setupEvents')
if (setupEvents.handleSquirrelEvent()) {
// squirrel event handled and app will exit in 1000ms, so don't do anything else
return;
}
const {
app,
BrowserWindow,
ipcMain
} = require('electron')
const path = require('path')
const url = require('url')
var server = require("./mysql");
let win
function createWindow() {
win = new BrowserWindow()
// load the dist folder from Angular
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}));
win.maximize();
// Open the DevTools optionally:
// win.webContents.openDevTools();
win.on('closed', () => {
win = null
});
// console.log(win.webContents.executeJavaScript('alert("world!");send();'));
}
console.log("ipc");
ipcMain.on('synchronous-message', (event, arg) => {
console.log(arg); // prints "ping"
event.returnValue = 'pong';
});
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (win === null) {
createWindow()
}
})
Now, for connecting with MySQL, we need to create a Node.JS mini server. create a mysql.js file inside Electron folder.
// mysql.js
var http = require("http");
var mysql = require("mysql");
var fs = require("fs");
var jsonContent = null;
const prompt = require('electron-prompt');
var userdata = {
"host": '',
"port": '',
"username": '',
"password": '',
"database": ''
};
onLoad();
function onLoad() {
// console.log("main window : " + mainWindow);
var connection = null;
try {
//This will prompt user for Database details one time.
var contents = fs.readFileSync(process.env.APPDATA + "/Mcare/sql-config.json");
jsonContent = JSON.parse(contents);
connection = mysql.createConnection({
host: jsonContent.host,
port: jsonContent.port,
user: jsonContent.username,
database: jsonContent.database,
password: jsonContent.password,
debug: false
});
} catch (ex) {
var dummy = JSON.stringify(userdata);
fs.writeFileSync(process.env.APPDATA + "/AngularElectronMySQL/sql-config.json", dummy);
onLoad();
}
connection.connect(function (err) {
if (err) {
console.error("error connecting: " + err.stack);
prompt({
title: 'Sql Config',
label: 'Host:',
value: jsonContent.host,
inputAttrs: {
type: 'text'
},
type: 'input',
})
.then((h) => {
if (h) {
userdata.host = h;
prompt({
title: 'Sql Config',
label: 'Port:',
value: jsonContent.port,
inputAttrs: {
type: 'text'
},
type: 'input',
})
.then((p) => {
if (p) {
userdata.port = p;
prompt({
title: 'Sql Config',
label: 'Username:',
value: jsonContent.username,
inputAttrs: {
type: 'text'
},
type: 'input',
})
.then((u) => {
if (u) {
userdata.username = u;
prompt({
title: 'Sql Config',
label: 'Password:',
value: jsonContent.password,
inputAttrs: {
type: 'password'
},
type: 'input',
})
.then((w) => {
userdata.password = w;
prompt({
title: 'Sql Config',
label: 'Database:',
value: jsonContent.database,
inputAttrs: {
type: 'text'
},
type: 'input',
})
.then((d) => {
if (d) {
userdata.database = d;
var json = JSON.stringify(userdata);
fs.writeFileSync(process.env.APPDATA + "/Mcare/sql-config.json", json);
onLoad();
}
})
.catch(console.error);
})
.catch(console.error);
}
})
.catch(console.error);
}
})
.catch(console.error);
}
})
.catch(console.error);
} else {
connection.end();
console.log("connected as id " + connection.threadId);
afterLoad();
}
});
}
function afterLoad() {
http
.createServer(function (req, res) {
var taskQueue = [];
var token = 0;
function checkExecuteFinished(queue) {
if (queue.length == 0) {
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify(data, null, 4));
res.end();
}
}
function readDataFromMySql(query, fn) {
taskQueue.push(token++);
var connection = mysql.createConnection({
host: jsonContent.host,
port: jsonContent.port,
user: jsonContent.username,
database: jsonContent.database,
password: jsonContent.password,
debug: false
});
connection.connect(function (err) {
if (err) {
console.error("error connecting: " + err.stack);
return;
}
console.log("connected as id " + connection.threadId);
});
connection.query(query, function (error, results, fields) {
if (error) throw error;
fn(results);
connection.end();
});
}
var data = [];
function cleanData() {
data = {
id: "",
name: ""
};
}
function getData(results) {
for (var i = 0; i < results.length; i++) {
cleanData();
data.id = results[i].Id;
data.name = results[i].Name;
}
}
taskQueue.shift();
checkExecuteFinished(taskQueue);
}
readDataFromMySql("select * from table", getData);
// console.log(JSON.stringify(location));
})
.listen(1024);
}
Run your Electron app. The first time it launches, it will prompt you for the database credentials and store them in a local config file.
Required npm Dependencies
For Node (Electron main process):
For copy.js: