Electron.js I can't do the Button action - electron

follow my source code, does anyone know more about Electron around here? I'm starting to study now and I'm trying to create an App that will update some files through an FTP, my problem initially I put a button on the acc.html page, but I can't do its action, I've looked for several examples but with none I can do , to work, it doesn't also arrive in the script that is defined inside the acc.html.
renderer.js
// include the ipc module to communicate with main process.
const ipcRenderer = require('electron').ipcRenderer;
console.log("Chamou O Renderer.js");
document.getElementById('buttonBaixarACCCustoms').open = () => {
//ipcRenderer.send('openFile', {})
console.log("Entrou No IPCRender");
ipcRenderer.send("btnclick", arg); // ipcRender.send will pass the information to main process
}
/*
const btnclick = document.getElementById('buttonBaixarACCCustoms');
btnclick.addEventListener('onclick', function () {
console.log("Entrou No IPCRender");
var arg ="secondparam";
//send the info to main process . we can pass any arguments as second param.
ipcRenderer.send("btnclick", arg); // ipcRender.send will pass the information to main process
});
*/
app.js
const {app, BrowserWindow, ipcMain, } = require('electron');
const url = require("url");
const path = require("path");
const ftp = require("basic-ftp");
let appWindow;
function initWindow() {
appWindow = new BrowserWindow({
width: 1000,
height: 800,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
webviewTag: true
}
})
// Electron Build Path
appWindow.loadURL('file://' + __dirname + '/electron-tabs.html');
/*
appWindow.loadURL(
url.format({
pathname: path.join(__dirname, `/dist/index.html`),
protocol: "file:",
slashes: true
})
);
*/
// Initialize the DevTools.
appWindow.webContents.openDevTools();
appWindow.on('ready-to-show', function () {
appWindow.show();
appWindow.focus();
});
appWindow.on('closed', function () {
appWindow = null
})
}
app.on('ready', initWindow)
// Close when all windows are closed.
app.on('window-all-closed', function () {
// On macOS specific close process
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
if (win === null) {
initWindow();
example();
}
})
example()
async function example() {
console.log("Entrou No Example...");
const client = new ftp.Client()
client.ftp.verbose = true
try {
await client.access({
host: "127.0.0.1",
user: "topgunav",
password: "topgunav",
secure: false
})
debugger;
//console.log(await client.list());
//await client.uploadFrom("README.md", "README_FTP.md")
//await client.downloadTo("README_COPY.md", "README_FTP.md")
}
catch(err) {
console.log(err)
}
client.close()
}
//ipcMain.on will receive the “btnclick” info from renderprocess
ipcMain.on("btnclick", function (event, arg) {
console.log("Chegou Aqui No IPCMain");
//create new window
/*
var newWindow = new BrowserWindow({ width: 450, height: 300, show:
false,webPreferences: {webSecurity: false,plugins:
true,nodeIntegration: false} }); // create a new window
var facebookURL = "https://www.facebook.com"; // loading an external url. We can
load our own another html file , like how we load index.html earlier
newWindow.loadURL(facebookURL);
newWindow.show();
*/
// inform the render process that the assigned task finished. Show a message in html
// event.sender.send in ipcMain will return the reply to renderprocess
//event.sender.send("btnclick-task-finished", "yes");
});
acc.html
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body style="margin:0">
<h1 style="text-align: center;">Sicronização De CarSet's</h1>
<div class="mb-3">
<label for="caminhoACCCustoms" class="form-label">Informe O Caminho Da Pasta Customs</label>
<input type="text" class="form-control form-control-lg" type="text" id="caminhoACCCustoms" placeholder="C:\Users\eders\OneDrive\Documentos\Assetto Corsa Competizione\Customs">
</div>
<select class="form-select form-select-lg mb-3" aria-label=".form-select-lg example">
<option selected>Informe A Temporada Que Deseja Sicronizar</option>
<option value="1">T1</option>
<option value="2">T2</option>
<option value="3">T4</option>
<option value="3">T5</option>
</select>
<button type="button" class="btn btn-primary btn-lg" id="buttonBaixarACCCustoms">Baixar</button>
</body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/#popperjs/core#2.10.2/dist/umd/popper.min.js" integrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.min.js" integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13" crossorigin="anonymous"></script>
<script src="../../renderer.js"></script>
</html>
electron-tabs.html
<!DOCTYPE html>
<html>
<head>
<title>SuperSync</title>
<link rel="stylesheet" href="./node_modules/electron-tabs/electron-tabs.css">
</head>
<body style="margin:0">
<div class="etabs-tabgroup">
<div class="etabs-tabs"></div>
<div class="etabs-buttons"></div>
</div>
<div class="etabs-views"></div>
</body>
<script src="./renderer.js"></script>
<script>
const TabGroup = require('electron-tabs');
let tabGroup = new TabGroup();
tabGroup.addTab({
title: "Assetto Corsa Competizione",
src: "./src/acc/acc.html",
visible: true,
active: true
});
</script>

Understanding and implementing Inter-Process Communication
can be tricky. You need a good understanding of
the Process Model
and Context Isolation.
In the below code I have greatly simplified your questions code to show only what is required to get a working IPC
process.
The purpose of this preload.js script is to define 'channel names' and the implementation of IPC between the main
process and render process(es).
This preload.js script can be included in all your window creation scripts. IE: webPreferences: { preload: 'preload.js' }.
preload.js (main process)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'buttonClick' // Channel name
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
This preload.js script can be used in your main process and render process(es) as shown below.
/**
* Render --> Main
* ---------------
* Render: window.ipcRender.send('channel', data); // Data is optional.
* Main: electronIpcMain.on('channel', (event, data) => { methodName(data); })
*
* Main --> Render
* ---------------
* Main: windowName.webContents.send('channel', data); // Data is optional.
* Render: window.ipcRender.receive('channel', (data) => { methodName(data); });
*
* Render --> Main (Value) --> Render
* ----------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
*
* Render --> Main (Promise) --> Render
* ------------------------------------
* Render: window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
* Main: electronIpcMain.handle('channel', async (event, data) => {
* return await promiseFunctionName(data)
* .then(() => { return result; })
* });
*/
During window creation make sure to include your preload.js script. Then listen for a message on the 'buttonClick'
channel.
main.js (main process)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js') // Include the preload.js script
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// Listen for a message on the 'buttonClick' channel
electronIpcMain.on('buttonClick', (event, data) => {
console.log(data); // Testing
})
Lastly, listen for a click event on the button, gather the data and send it via the correct IPC channel (name) to
the main process for processing.
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
</head>
<body>
<h1>Sicronização De CarSet's</h1>
<label for="textField">Informe O Caminho Da Pasta Customs</label>
<input id="textField" type="text" placeholder="C:\Custom\Path"><br>
<label for="selectField">Informe A Temporada Que Deseja Sicronizar</label>
<select id="selectField">
<option value="1">T1</option>
<option value="2">T2</option>
<option value="3">T3</option>
<option value="4">T4</option>
</select><br>
<button id="button" type="button">Baixar</button>
</body>
<script>
document.getElementById('button').addEventListener('click', () => {
let data = {
textField: document.getElementById('textField').value,
selectField: document.getElementById('selectField').value
}
console.log(data); // Testing
window.ipcRender.send('buttonClick', data);
});
</script>
</html>

Related

How do I display console log in electron app

I'm currently creating a JS Desktop App using Electron. I'm able to get everything functional how I want it, but I want to be able to update the users on certain tasks and also display errors in the app itself.
Is there any way to add a section (terminal if you will) or something similar inside the UI, so I can log things out to the user?
Providing application feedback to your user is as simple as having a statusUpdate function in your main process send
a message (via your preload.js script) to your render process. Within your render process, listen for a message on the
assigned channel and once received, update the content of your DOM element.
main.js (main process)
// Import required electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
// Import required Node modules
const nodePath = require('path');
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
statusTest();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
function statusTest() {
let counter = 1;
let message;
setInterval(() => {
statusUpdate(`Status message ${counter} from main process.`);
counter++;
}, 1000);
}
function statusUpdate(message) {
window.webContents.send('statusMessage', message);
}
preload.js (main process)
// Import the necessary Electron modules
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// Exposed protected methods in the render process
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods
'ipcRenderer', {
// From main to render
statusMessage: (message) => {
ipcRenderer.on('statusMessage', message);
}
}
);
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
</head>
<body>
<label for="status">Status: </label>
<textarea id="status" cols="40" rows="10" disabled></textarea>
</body>
<script>
let status = document.getElementById('status');
window.ipcRenderer.statusMessage((event, message) => {
status.value += message + '\n'; // Append message
status.scrollTop = status.scrollHeight; // Show last entry
});
</script>
</html>

sending value url from ipcRender to ipcMain

Hey i am making a project where i want to send data to ipc main
my index.js
const {app, BrowserWindow , ipcMain} = require('electron')
const ejse = require('ejs-electron')
app.on('ready', () => {
ipcMain.on("getUrl",(event,url)=>{
console.log(url);
})
mainWindow = new BrowserWindow({
autoHideMenuBar: true,
icon: __dirname + '/logo.ico',
webPreferences: {
devTools: false,
nodeIntegration:true,
webviewTag:true
}
})
mainWindow.loadURL('file://' + __dirname + '/files/index.ejs');
})
index.ejs contains a anchor tag with value as link
<button id="dw" style="display: none;"><a class="button" id="dwl" onclick="heal()" value="#">Download</a></button>
<script src=download.js></script>
download.js i am able to alert the link but ipcRender not working
const { ipcRenderer } = require("electron");
function heal(){
var url = document.getElementById("dwl").value;
alert(url);
ipcRenderer.send("getUrl",url);
}
i am not able to send url from ejs file to main index.js file alert is working in download.js but some error in sending data to main file console.log is not working
The use of contextBridge within your preload.js script safely exposes API's you approve for use in your render
scripts.
Whist understanding how all this works can be challenging at first, becoming familiar with the below content and the
following code should put you in good stead for a solid understanding of how you can wire up your application.
Context Isolation
contextBridge
Enabling Content Isolation for remote content
Inter-Process Communication
ipcMain
ipcRenderer
webContents - contents.send(channel, ...args)
Some people like to implement concrete functions within their preload.js script(s). In this example,
the preload.js script is only used to configure whitelisted channel names for use between the main process and render
process(es). This keeps your code seperated. IE: Separation of concerns.
Let's set the channel name getUrl within the ipc.render.send array.
preload.js (main process)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'getUrl' // Channel name
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Let's set the webPreferences.contextIsolation value to true and specify the webPreferences.preload path.
webPreferences.nodeIntegration can be set to false, but in doing so webPreferences.contextIsolation should be
set to true to "truly enforce strong isolation and prevent the use of Node primitives".
Lastly, let's listen for a message from the render process on the getUrl channel.
main.js (main process)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// Listen for a message on the 'getUrl' channel
electronIpcMain.on('getUrl', (event, url) => {
console.log(url);
})
This file is fairly standard and easy to understand. Just adapt code for use with .ejs
Added Javascript code in between <script> tags for sake of brevity.
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
</head>
<body>
<input type="button" id="dwl" value="#">
</body>
<script>
let button = document.getElementById('dwl');
button.addEventListener('click', () => {
// Send URL on the 'getUrl' channel
window.ipcRender.send('getUrl', button.value);
});
</script>
</html>

Electron - Uncaught ReferenceError: require is not defined

I have already searched in the others posts of stackflow with the same error, but aparently my problem is not about nodeIntegration parameter. Because, even the paramater of nodeIntegration is set true, i still get the undefined problem of require in the main-login.js file! PS: All files is in the same root and directory...
main.js
const { app, BrowserWindow, remote, ipcMain } = require("electron");
const path = require("path");
//Funcionalidade de criar window
function createWindow() {
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, "preload.js")
},
});
win.maximize();
win.loadFile("index.html");
}
//Chamada da funcionalidade de criação do window
app.whenReady().then(() => {
createWindow();
app.on("activate", function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
//Fechamento do app quando todos os windows forem fechados
app.on("window-all-closed", function () {
if (process.platform !== "darwin") app.quit();
});
//Aprovação da validação do login
ipcMain.on("validation", function (e, item) {
console.log(item);
})
index.html
<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
<div class="container-login100" style="background-image: url('images/pdv-login.jpg');">
...
<div class="container-login100-form-btn">
<button class="login100-form-btn">
Entrar
</button>
</div>
</div>
</div>
<!--===============================================================================================-->
<script src="vendor/jquery/jquery-3.2.1.min.js"></script>
...
<script src="vendor/countdowntime/countdowntime.js"></script>
<!--===============================================================================================-->
<script src="main-login.js"></script>
</body>
</html>
main-login.js
const { ipcRenderer } = require('electron');
(function ($) {
"use strict";
...
function hideValidate(input) {
var thisAlert = $(input).parent();
$(thisAlert).removeClass("alert-validate");
$(thisAlert).find(".btn-hide-validate").remove();
}
})(jQuery);

Getting absolute file path from file input in isolated environment in Electron app

I am starting an Electron based desktop app, which extends existing web app capabilities.
I am interested whether I can get the absolute file path from file input. HTML is displayed inside a BrowserView, which has nodeIntegration disabled and contextIsolation enabled.
My simple test does return a valid file path in this case, though my understanding is that it shouldn’t.
Maybe I am missing something? That looks like a security vulnerability…
Can I count in my desktop app that Electron will return a valid file path in a scenario similar to the one in the code below?
main.js
const { app, BrowserWindow, BrowserView } = require('electron');
let mainWindow;
const createWindow = () => {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
});
let view = new BrowserView({
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
})
mainWindow.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 800, height: 600 })
view.webContents.loadURL('http://localhost:8000/index.html')
mainWindow.on('closed', () => {
mainWindow = null;
});
};
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
index.html
<html>
<script type="text/javascript">
function _on_file_change_() {
file_input = document.getElementById('file1');
console.log(file_input.files[0]);
}
</script>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
</head>
<body>
<input type="file" id='file1' onchange="_on_file_change_(this.files)"/>
</body>
</html>

Electron + Vue (SPA): Not allowed to load local resource: file:///app/src/Products.vue

I have an Electron app + Vue for rooting. I am having problems loading the content into a newly opened window. The window is launched from a Vue component. When it opens I get a blank window and:
Not allowed to load local resource: file:///app/src/Products.vue
I have tried different methods mentioned on stackoverflow but the error still persists.
<style scoped>
</style>
<template>
<div class="container-fluid">
Parent window...
<button type="submit" class="btn btn-primary" v-on:click="add">+ Add Product</button>
</div>
</template>
<script>
export default {
methods: {
add: function () {
const remote = require('electron').remote
const BrowserWindow = remote.BrowserWindow
let win
win = new BrowserWindow({
height: 600,
width: 800
})
win.loadURL(`file://${__dirname}/app/src/Products.vue`)
win.openDevTools()
}
}
}
</script>
In your case, child window must be created from the main process to launch a child window with local resources in Electron. You can use ipc (ipcMain, ipcRenderer) for this.
For example,
In main process :
function createChildWindow(payload) {
let child = new BrowserWindow({ parent :mainWindow
});
child.loadURL(url.format({
pathname: path.join(__dirname, 'child.html'),
protocol: 'file:',
slashes: true,
}));
child.once('ready-to-show', () => {
child.show()
});
}
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
createChildWindow(arg);
});
In renderer process(web page) :
const {ipcRenderer} = window.require('electron')
async launchChildWindow(){
ipcRenderer.send('asynchronous-message', '<payload>');
}
You can also write custom events like this,
// Renderer process
ipcRenderer.invoke('some-name', someArgument).then((result) => {
// ...
})
// Main process
ipcMain.handle('some-name', async (event, someArgument) => {
const result = await doSomeWork(someArgument)
return result
})

Resources