Electron property inacessible in render.js - javascript

I'm trying to learn how to build electron apps and I found this Youtube tutorial Build a Desktop App with Electron... But Should You? on building a simple screen recording app but i encountered this error
Uncaught TypeError: Cannot destructure property 'Menu' of 'remote' as it is undefined.
at render.js:9
Here is the exact code from the tutorial code
const videoElement = document.querySelector('video');
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const videoSelectBtn = document.getElementById('videoSelectBtn');
videoSelectBtn.onclick = getVideoSources;
const {desktopCapturer,remote} = require('electron');
const {Menu} = remote;
async function getVideoSources(){
const inputSources = await desktopCapturer.getSources({
types:['window','screen']
});
const videoOptionsMenu = Menu.buildFromTemplate(
inputSources.map(source =>{
return{
label:source.name,
click:()=>selectSource(souce)
}
})
);
videoOptionsMenu.popup();
}
What I'm I doing wrong?

in new version of electron. you need to allow your electron app to use remote modules.
add enableRemoteModule: true flag in your main electron code.
mainWindow = new BrowserWindow({
width: 1280,
height: 960,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
},
});

Related

`loadURL` in Electron has error "window.require is not a function" only this website

NOTE: Electron 17.0.1
I'm so confuse about to use the win.loadURL function in Electron.
For main purpose, to create a dark theme for chat application.
I want to loadURL in Electron and inject CSS to change to dark theme.
BUT when I use loadURL and then run Electron app.
It will show an error "window.require is not a function"
and "Uncaught TypeError: Cannot read properties of undefined (reading 'instance')"
here is my code:
// main.js
const { app, BrowserWindow } = require("electron");
const path = require("path");
function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
},
});
win.webContents.openDevTools();
win.loadURL("https://seatalkweb.com/");
}
app.whenReady().then(() => {
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
// preload.js
window.addEventListener("DOMContentLoaded", () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) element.innerText = text;
};
for (const type of ["chrome", "node", "electron"]) {
replaceText(`${type}-version`, process.versions[type]);
}
});
and this is an error:
I do not know why it has an error only this website when open in electron? (no problem if open in chrome)
this website is https://seatalkweb.com/
and it fine if I use other URL instead (such as google.com, facebook.com, etc.)
How can I fix it ?
and could somebody can explain me about this problem ?
Seems weird like a require is called is your page but it's maybe an Electron call du to the LoadUrl... If it's a require problem you may need to activate the Node integration..
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
nodeIntegration: true
},
});
If it's not from Electron Node integration, then in may be a problem in your web page script.

Noob to electronjs: Question about contextBridge / appropriate usage

I'm quite new to ElectronJS and much of my learning has come from examples and docs since it seems many tutorials out there are outdated especially due to the addition of enforcing nodeIntegration: false and contextIsolation: true.
My research has led me down the path of preload scripts, which by themselves still open themselves up to vulnerabilities, which then led me to contextBridge via preload scripts.
As a disclaimer, what I'm doing is probably unnecessary since I'm not actually connecting to any third-party and everything is local, so theoretically I could edit the webPreferences for nodeIntegration and contextIsolation. However, for my own learning purposes as well as best practice, I felt it necessary to understand how to do what I want to do and expose APIs such as require, fs, path, os etc. without using a "deprecated" feature.
Mainly wanted to post here to see if this is the proper method of using contextBridge.
main.js
const { app, BrowserWindow, Menu, ipcMain } = require("electron");
const path = require("path");
const os = require("os");
...
let mainWindow;
function createMainWindow() {
mainWindow = new BrowserWindow({
title: "Test",
icon: `${__dirname}/assets/icons/Icon_256x256.png`,
width: isDev ? 800 : 500,
height: 600,
resizable: isDev,
backgroundColor: "white",
webPreferences: {
preload: `${__dirname}/preload.js`,
nodeIntegration: false,
contextIsolation: true,
},
});
...
ipcMain.handle("get-file-path", async (event) => {
const filePath = path.join(os.homedir(), "test");
return filePath;
});
...
preload.js
const { ipcRenderer, contextBridge } = require("electron");
contextBridge.exposeInMainWorld("api", {
getFilePath: async () => {
const res = await ipcRenderer.invoke("get-file-path");
return res;
},
});
renderer.js
window.addEventListener("DOMContentLoaded", async () => {
document.getElementById("output-path").innerText =
await window.api.getFilePath();
});
It works as intended. From my understanding of what is happening here is by using contextBridge with a callback method I'm essentially keeping everything isolated within that function and not exposing anymore that what's within that closure--is that correct?
Once again, I'm really new (1 day into electron essentially). I've only been 'developing' for 7 months or so.
Would this also be valid.. offloading to another js file and exposing those functions through context bridge?
test.js
const path = require("path");
const os = require("os");
const getFilePath = () => {
const filePath = path.join(os.homedir(), "test");
return filePath;
};
module.exports = {
path: getFilePath,
};
preload.js
const { contextBridge } = require("electron");
const mainFunctions = require("./test.js");
const { path } = mainFunctions;
contextBridge.exposeInMainWorld("api", {
getFilePath: path(),
});
Both give me the outcome I expect.

how to send value as a parameter to loadURL() of electron main.js file from node js file?

const electron = require("electron");
const { app, BrowserWindow } = electron;
const path = require("path");
​
function createWindow() {
// Create the browser window.
​
//window 1
let win1 = new BrowserWindow({
width: 1000,
height: 600,
});
​
win1.loadURL("https://twitter.com/");
win1.on("closed", () => {
win1 = null;
});
}
app.on("ready", createWindow);
i need to get url from frontend and access in node.js and pass to main.js loadURL() as a parameter.
What do you mean by this get url from frontend and access in node.js.
If my recognition is correct, I think you are going to read url from the renderer and send this to Electron main process to create BrowserWindow based on that url.
So communication between renderer and main.
Use this ipcRenderer and ipcMain.
You can send the url using ipcRenderer like this
...
const { ipcRenderer } = require('electron');
function sendUrlToMain(url) {
ipcRenderer.sendSync('sendUrlToMain', url);
return res;
}
...
Then add this to main.js
ipcMain.on('sendUrlToMain',(event, arg) => {
let win1 = new BrowserWindow({
width: 1000,
height: 600,
});
​
win1.loadURL(arg);
win1.on("closed", () => {
win1 = null;
});
});

Error after updating electron and changin nodeIntegration to true

I recently went back to an old project and updated my electron version. I found online that electron now requires you to add in nodeIntegration: true in order to be able to import electron in your render process. I added this in as seen below, however, I am getting the following error after doing so and am unsure how to resolve this.
// Module Imports
const {app,BrowserWindow,dialog,ipcMain,remote} = require('electron')
var handlers = require('./routelist.js');
var dns = require('dns').promises;
var path = require('path');
var Connection = require('tedious').Connection;
var sql = require('sequelize');
var axios = require('axios');
let win
function createWindow () {
win = new BrowserWindow({webPreferences: {nodeIntegration: true}, width: 1730, height: 900, frame: false})
win.loadFile('./render/index.html')
win.webContents.openDevTools()
win.on('closed', () => {
win = null
})
}
app.on('ready', function() {
createWindow()
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (win === null) {
createWindow()
}
})
Error:
Uncaught (in promise) TypeError [ERR_INVALID_ARG_TYPE]: The "id" argument must be of type string. Received type object
at validateString (internal/validators.js:112)
at Module.require (internal/modules/cjs/loader.js:768)
at require (internal/modules/cjs/helpers.js:68)
at vendor-bundle.js:7033
at new Promise (<anonymous>)
at createLoader (vendor-bundle.js:7032)
Let me know if I can provide further information.
electron now requires you to add in nodeIntegration: true in order to be able to import electron in your render process
You cannot directly use electron in a renderer process, use electron.remote:
const { remote } = require('electron');
const window = new remote.BrowserWindow({ width: 800, height: 600 });
See https://electronjs.org/docs/api/remote
After Using the chrome debugger for a really long time trying to locate when this error is happening it isn't related to electron in the slightest. The vendor bundle I am using is passing in an incorrect value for one of it's dependencies
Thanks to everyone for your time.

How to use electron's app.getPath() to store data?

I want to store images on the users computer, so I figure it should be stored in users data folder, as described here.
app.getPath(name)
name. Returns String - A path to a special directory or file associated with name. On failure an Error is thrown. You can request the following paths by the name:
home User's home directory
appData Per-user application data directory, which by default points to:
%APPDATA% on Windows
$XDG_CONFIG_HOME or ~/.config on Linux
~/Library/Application Support on macOS
userData The directory for storing your app's configuration files, which by default it is the appData directory appended with your app's name.
...
This is what I think you're supposed to do:
const app = require('electron');
alert(app.getPath('userData'));
But I get "getPath is not a function". I am not sure where to put it. It does not work from my html file or the renderer file, and I'm not sure how to use it from the main file because that's not linked to the web page.
Since the remote method is being considered deprecated, as shown here, I'd suggest you do this:
const {app} = require('electron');
console.log(app.getPath('userData'));
remote is considered dangerous.
app.getPath will be always available in main process.
Here is how to do it in renderer process without using remote (electron 7+)
Basically you have to use invoke in renderer.
in main
ipcMain.handle('read-user-data', async (event, fileName) => {
const path = electron.app.getPath('userData');
const buf = await fs.promises.readFile(`${path}/${fileName}`));
return buf;
})
in renderer
ipcRenderer.invoke('read-user-data', 'fileName.txt').then(
result => doSomething()
);
Here is what I use when I need to switch between dev and release
const electron = require('electron');
export const userDataPath = (electron.app || electron.remote.app).getPath(
'userData'
);
Another way to prevent the error "getPath is not a function" is to make the code work both in the renderer process and the main process:
const electron = require('electron');
const configDir = (electron.app || electron.remote.app).getPath('userData');
console.log(configDir);
I had trouble with app.getPath('userData') to save/load config files, etc and ended up using OS specific env vars in the meantime:
const getAppBasePath = () => {
//dev
if (process.env.RUN_ENV === 'development') return './'
if (!process.platform || !['win32', 'darwin'].includes(process.platform)) {
console.error(`Unsupported OS: ${process.platform}`)
return './'
}
//prod
if (process.platform === 'darwin') {
console.log('Mac OS detected')
return `/Users/${process.env.USER}/Library/Application\ Support/${YOUR_APP_NAME}/`
} else if (process.platform === 'win32') {
console.log('Windows OS detected')
return `${process.env.LOCALAPPDATA}\\${YOUR_APP_NAME}\\`
}
}
if you wanna do it in renderer process
try this,it is work for me
// main.js
const electron = require('electron')
const electronRemote = process.type === 'browser' ? electron :
require('#electron/remote')
const { app, ipcMain, Menu, globalShortcut } = require('electron')
const BrowserWindow = electronRemote.BrowserWindow
const isDev= require('electron-is-dev')
const { initialize, enable } = require('#electron/remote/main')
initialize()
let mainWindow
app.on('ready', ()=>{
mainWindow = new BrowserWindow({
width: 1024,
height: 600,
minWidth:600,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false
}
})
enable(mainWindow.webContents)
})
render process
// render process
const { app } = window.require('#electron/remote')
const savedPath = app.getPath('userData')

Categories

Resources