When trying to open the window from the menu, an error like this comes up: "Attemping to call a function in a renderer window that has been closed or released", but the other windows are called without any problem.
const ventana = document.getElementById("ventana");
const contrasena = document.getElementById("contra");
const remote = require("electron").remote;
const Menu = remote.Menu;
const BrowserWindow = remote.BrowserWindow;
const url = require("url");
const path = require("path");
let secundario
let recuperaVentana
let add
ventana.addEventListener("click", () => {
var usuario = document.getElementById("user").value;
var pass = document.getElementById("pass").value;
if(usuario != "" & pass != ""){
createBrowserWindow();
} else {
alert("No ha llenado los campos");
}
});
contrasena.addEventListener("click", () => {
recuperarBrowserWindow();
})
function createBrowserWindow() {
secundario = new BrowserWindow({
height: 600,
width: 800
});
secundario.loadURL(url.format({
pathname: path.join(__dirname, "./prefs.html"),
protocol: "file",
slashes: true
}));
//secundario.webContents.openDevTools();
//menu
const menuSec = Menu.buildFromTemplate(templateMenu);
Menu.setApplicationMenu(menuSec);
cerrar();
}
function recuperarBrowserWindow(){
recuperaVentana = new BrowserWindow ({
title: "Recuperar"
})
recuperaVentana.loadURL(url.format({
pathname: path.join(__dirname, "./recuperar.html"),
protocol: "file",
slashes: true
}))
recuperaVentana.setMenu(null);
}
//cerrando ventana
function cerrar(){
window.close();
}
//creando menus
const templateMenu = [
{
label: "Archivo",
submenu: [
{
label: "Nuevo",
accelerator: "Ctrl+N",
click(){
addBrowserWindow();
}
},
{
label: "Abrir",
accelerator: "Ctrl+O"
},
{
label:"Guardar",
accelerator:"Ctrl+G"
},
{
type: "separator"
},
{
label:"Salir"
}
]
},
{
label: "Ayuda"
}
];
function addBrowserWindow() {
add = new BrowserWindow({
title: "Agregar"
})
add.loadURL(url.format({
pathname: path.join(__dirname, "./agregar.html"),
protocol: "file",
slashes: true
}))
}
When trying to call the function "addBrowserWindow()" the above mentioned error is shown
This is a fairly concise version of code that creates a second window on selecting a submenu:
const electron = require('electron')
const { app, BrowserWindow, Menu } = electron
let mainWindow
let secondWindow
app.on('ready', () => {
console.log('Starting Node version: ' + process.version)
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: true,
},
})
mainWindow.loadURL(`file://${__dirname}/main.html`)
mainWindow.on('closed', () => app.quit())
const mainMenu = Menu.buildFromTemplate(menuTemplate)
Menu.setApplicationMenu(mainMenu)
})
function createSecondWindow() {
secondWindow = new BrowserWindow({
width: 300,
height: 200,
title: 'Add New Todo',
})
secondWindow.loadURL(`file://${__dirname}/add.html`)
}
const menuTemplate = [
{
label: 'File',
submenu: [
{
label: 'New ToDo',
click() {
createSecondWindow()
},
},
{
label: 'Quit',
accelerator: process.platform === 'darwin' ? 'Command+Q' : 'Ctrl+Q',
click() {
app.quit()
},
},
],
},
]
if (process.platform === 'darwin') {
menuTemplate.unshift({})
}
Note that by default when you close the main window and the second window will still be present - which is weird behaviour rarely needed. The solution to that is to ensure you call app.exit() when the main window is closed.
Related
I am building an Electron app that gets a certain kind of json file from the user and logs all the data from it. But I am getting an error about getting undefined from the quantity:
Json File:
{
"tiers": [
{
"trades": [
{
"wants": [
{
"item": "minecraft:string",
"quantity": {
"min": 15,
"max": 20
}
}
],
"gives": [
{
"item": "minecraft:emerald"
}
]
},
{
"wants": [
{
"item": "minecraft:emerald"
}
],
"gives": [
{
"item": "minecraft:arrow",
"quantity": {
"min": 8,
"max": 12
}
}
]
}
]
},
{
"trades": [
{
"wants": [
{
"item": "minecraft:gravel",
"quantity": 10
},
{
"item": "minecraft:emerald",
"quantity": 1
}
],
"gives": [
{
"item": "minecraft:flint",
"quantity": {
"min": 6,
"max": 10
}
}
]
},
{
"wants": [
{
"item": "minecraft:emerald",
"quantity": {
"min": 2,
"max": 3
}
}
],
"gives": [
{
"item": "minecraft:bow"
}
]
}
]
}
]
}
And this is the main.js:
const { app, BrowserWindow, ipcMain, dialog, ipcRenderer, globalShortcut } = require('electron');
const { autoUpdater } = require('electron-updater');
const path = require('path');
const Store = require('./classes/Store.js');
const { electron } = require('process');
const { setTimeout } = require('timers');
// import Vue from 'vue';
// import Vuetify from 'vuetify';
// import "vuetify/dist/vuetify.min.css";
// Vue.use(Vuetify);
let win;
let loadingScreen;
const store = new Store({
configName: 'settings',
defaults: {
windowBounds: {
width: 800,
height: 600,
x: 0,
y: 0,
},
isMaximized: false,
fullscreen: false
}
});
function createLoadingScreen() {
loadingScreen = new BrowserWindow(Object.assign({
width: 200,
height: 220,
frame: false,
transparent: true,
icon: 'build/icons/icon.png',
webPreferences: {
worldSafeExecuteJavaScript: true
}
}));
loadingScreen.setResizable(false);
loadingScreen.loadURL('file://' + __dirname + '/extraWindows/loadingScreen/loading.html');
loadingScreen.setOverlayIcon('build/icons/icon.png', "Route");
loadingScreen.on('closed', () => loadingScreen = null);
loadingScreen.webContents.on('did-finish-load', () => {
loadingScreen.show();
});
}
function createWindow() {
let width = store.get('windowBounds.width');
let height = store.get('windowBounds.height');
let x = store.get('windowBounds.x');
let y = store.get('windowBounds.y');
let isMaximized = store.get('isMaximized');
win = new BrowserWindow({
width,
height,
x,
y,
icon: 'build/icons/icon.png',
frame: false,
titleBarStyle: "hidden",
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true,
webSafeExecuteJavaScript: true
},
show: false
})
win.setOverlayIcon('build/icons/icon.png', "Route");
if(isMaximized == true) {
win.maximize();
}
win.loadFile('index.html');
win.on('closed', function() {
win = null;
settingScreen = null;
});
win.webContents.on('did-finish-load', () => {
if(loadingScreen) {
loadingScreen.close();
win.show();
}
})
win.on('resize', () => {
store.set('windowBounds', win.getBounds());
});
win.on('move', () => {
store.set('windowBounds', win.getBounds());
})
win.on('maximize', () => {
store.set('isMaximized', true);
})
win.on('unmaximize', () => {
store.set('isMaximized', false);
})
win.on('show', () => {
win.setFullScreen(store.get('fullscreen'));
})
win.once('ready-to-show', () => {
autoUpdater.checkForUpdatesAndNotify();
})
}
function createSettings() {
settingScreen = new BrowserWindow({
width: 600,
height: 800,
frame: false,
parent: win,
modal: true,
titleBarStyle: "hidden",
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true,
worldSafeExecuteJavaScript: true
},
show: false
})
settingScreen.loadFile('extraWindows/settingsScreen/settings.html');
settingScreen.on('close', (event) => {
event.preventDefault();
settingScreen.hide();
})
}
app.on('ready', () => {
createLoadingScreen();
setTimeout(() => {
createWindow();
createSettings();
}, 5000); // Set to 5000
});
app.on('window-all-closed', () => {
if(process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if(BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
})
app.whenReady().then(() => {
globalShortcut.register('CmdOrCtrl+O', () => {
win.webContents.send('openFile');
})
globalShortcut.register('CmdOrCtrl+D', () => {
win.webContents.send('closeFile');
})
})
// These are all of the ipc functions needed for Route to work
ipcMain.handle('viewSettings', (event, arg) => {
if(arg === "true") {
settingScreen.show();
} else if(arg === "false") {
settingScreen.hide();
}
});
ipcMain.handle('changeSettings', (event, arg) => {
if(arg === "fullScreen") {
if(win.isFullScreen() == false) {
win.setFullScreen(true);
store.set('fullscreen', true);
} else {
win.setFullScreen(false);
store.set('fullscreen', false);
}
// Fill in the data for fullscreen
} else if(arg === 'themechooser') {
// Fill in the data for themes
}
});
ipcMain.on('appVersion', (event) => {
event.sender.send('appVersion', { version: app.getVersion() });
});
ipcMain.on('restartApp', () => {
autoUpdater.quitAndInstall();
})
autoUpdater.on('updateAvailable', () => {
win.webContents.send('updateAvailable');
});
autoUpdater.on('updateDownloaded', () => {
win.webContents.send('updateDownloaded');
})
And this is the renderer.js:
const { app, ipcRenderer } = require('electron');
const Store = require('./classes/Store.js');
const remote = require('electron').remote;
const dialog = require('electron').remote.dialog;
const fs = require('fs');
const { S_IFDIR } = require('constants');
const { electron } = require('process');
const { version } = require('os');
let currentWindow = remote.getCurrentWindow();
let $ = function(selector) {
return document.querySelector(selector);
}
let projectArray = [];
// There can only be 5 projects max. Might add more.
let projectCount = 0;
document.querySelector('#tradeSetup').addEventListener('click', () => {
//Create a new setup.
alert("This works!");
});
let fileOpenerOptions = {
title: "Open Trade File",
defaultPath: "C:\\",
buttonLabel: "Start Coding",
properties: [ 'openFile' ],
filters: [
{ name: 'Json', extensions: ['json'] },
{ name: 'All FIles', extensions: ['*'] }
]
}
//Top bar buttons
function openFile() {
dialog.showOpenDialog(currentWindow, fileOpenerOptions).then(fileNames => {
if(fileNames == undefined || fileNames == null) {
console.log("No file selected.");
return;
} else {
let projectName = path.basename(fileNames.filePaths[0], path.extname(fileNames.filePaths[0]));
let rawFileData = fs.readFileSync(fileNames.filePaths[0]);
let fileData = JSON.parse(rawFileData);
console.log(fileData);
if(projectCount < 5) {
projectCount++;
createProject(projectName, fileData);
// Run code
} else if (projectCount == 5) {
alert('Support for more projects is not available yet!\nPlease wait until a later update to have more than 5 projects.');
}
}
});
}
function openSettings() {
ipcRenderer.invoke('viewSettings', "true");
}
function closeFile() {
if(document.querySelector('.activeProjectButton') == null) {
console.log("No project to close.");
} else {
let contine = confirm("Is this project the furthest project to the right? If not, please don't close it.\nThis is a bug that is being worked on.");
if(contine == true) {
document.querySelector('.activeProjectButton').remove();
document.querySelector('.activeWorkspace').remove();
projectCount--;
document.querySelectorAll('.inactiveProjectWorkspace').j--;
}
// Add a fun little secret for users
}
}
document.querySelector('#openFile').addEventListener('click', () => {
openFile();
});
document.querySelector('#openSettings').addEventListener('click', () => {
openSettings();
});
document.querySelector('#closeFile').addEventListener('click', () => {
closeFile();
})
// Create and switch projects
function createProject(name, projectData) {
// This is a beta feature for now. It is not finished
// Create the tab
let projectInProduction = document.createElement("button");
projectInProduction.id = "project" + projectCount + "Click";
projectInProduction.classList.add("invisButton");
projectInProduction.classList.add("inactiveProjectButton");
projectInProduction.classList.add("project" + projectCount);
projectInProduction.innerHTML += (name);
// Remove the welcome message
document.querySelector('.unHidden').classList.add('hidden');
document.querySelector('.unHidden').classList.remove('unHidden');
//Create the workspace
let workspaceInProduction = document.createElement("div");
workspaceInProduction.classList.add("inactiveWorkspace");
projectArray.push(projectData);
if(projectArray[projectCount - 1].tiers == null) {
alert("This is not a trade file!\nPlease use a trade file!");
} else {
$('.projects').appendChild(projectInProduction);
$('#jsonProjectContainer').appendChild(workspaceInProduction);
let table = document.createElement('table');
table.innerHTML += '<tr><th colspan="3">Buying</th><th colspan="2">Selling</th></tr>';
table.classList.add('table' + projectCount);
workspaceInProduction.appendChild(table);
let tiers = Object.keys(projectArray[projectCount - 1].tiers).length;
console.log(tiers);
for(let i = 0; i < tiers; i++) {
let tradesC = Object.keys(projectArray[projectCount - 1].tiers[i].trades).length;
for(let j = 0; j < tradesC; j++) {
let wants = Object.keys(projectArray[projectCount - 1].tiers[i].trades[j].wants);
let wantsC = Object.keys(projectArray[projectCount - 1].tiers[i].trades[j].wants).length;
let gives = Object.keys(projectArray[projectCount - 1].tiers[i].trades[j].gives);
for(let k = 0; k < wantsC; k++) {
let wantsItem = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].item;
let wantsMin = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity["min"];
let wantsMax = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity["max"];
let givesItem = projectArray[projectCount - 1].tiers[i].trades[j].gives[0].item;
console.log("Wants: " + wants[k]);
console.log("Min: " + wantsMin);
console.log("Max: " + wantsMax);
console.log("Item: " + wantsItem);
console.log("Gives: " + gives);
console.log("Item: " + givesItem);
}
}
}
}
// Add a onlclick function to make switching projects work
j = projectCount;
document.getElementById('project' + projectCount + 'Click').addEventListener('click', function(j) {
let buttonIndex = j;
let buttonNodes = $('.projects').children;
let workspaceNodes = $('#jsonProjectContainer').children;
for(let i = 0; i < buttonNodes.length; i++) {
if(i == (j - 1)) {
buttonNodes[i].classList.add('activeProjectButton');
buttonNodes[i].classList.remove('inactiveProjectButton');
workspaceNodes[i].classList.add('activeWorkspace');
workspaceNodes[i].classList.remove('inactiveWorkspace');
} else {
buttonNodes[i].classList.remove('activeProjectButton');
buttonNodes[i].classList.add('inactiveProjectButton');
workspaceNodes[i].classList.remove('activeWorkspace');
workspaceNodes[i].classList.add('inactiveWorkspace');
}
}
}.bind(null, j));
}
// Title bar scripts
ipcRenderer.on('openFile', () => {
openFile();
})
ipcRenderer.on('closeFile', () => {
closeFile();
})
// App updates
ipcRenderer.send('appVersion');
ipcRenderer.on('appVersion', (event, arg) => {
ipcRenderer.removeAllListeners('appVersion');
//version.innerText = ;
});
const updateNotifier = document.getElementById('updateNotifier');
const updateAvailability = document.getElementById('updateAvailability');
const restartButton = document.getElementById('restartButton');
ipcRenderer.on('updateAvailable', () => {
ipcRenderer.removeAllListeners('updateAvailable');
updateAvailability.innerText = 'A new version of Route is available. Downloading now...';
updateNotifier.classList.remove('hidden');
});
ipcRenderer.on('update_downloaded', () => {
ipcRenderer.removeAllListeners('updateDownloaded');
updateAvailability.innerText = 'Update downloaded. It will be installed on restart. Restart to continue.';
restartButton.classList.remove('hidden');
updateNotifier.classList.remove('hidden');
});
function restartApp() {
ipcRenderer.send('restartApp');
}
And this is the index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>route - Minecraft Addon Tool</title>
<!-- https://electronjs.org/docs/tutorial/security#csp-meta-tag -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<link href="index.css" type="text/css" rel="stylesheet" />
<link href="node_modules/#mdi/font/css/materialdesignicons.min.css" type="text/css" rel="stylesheet" />
</head>
<body>
<script>
// const electron = require('electron').remote;
const path = require('path');
const customTitleBar = require('custom-electron-titlebar');
const Menu = require('electron').remote.Menu;
const MenuItem = require('electron').remote.MenuItem;
let mainTitlebar = new customTitleBar.Titlebar({
backgroundColor: customTitleBar.Color.fromHex('#391B47'),
icon: 'build/icons/icon.png'
});
const menu = new Menu();
menu.append(new MenuItem({
label: 'Github',
click: () => {
currentWindow.webContents.on('new-window', require('electron').shell.openExternal('https://github.com/Gekocaretaker/route'));
}
}));
menu.append(new MenuItem({
label: 'Discord',
click: () => {
console.log("Hiding the link!");
}
}));
menu.append(new MenuItem({
label: 'File',
submenu: [
{
label: 'Open File',
accelerator: 'Ctrl+O',
click: () => {
openFile();
}
},
{
label: 'Close File',
accelerator: 'Ctrl+D',
click: () => {
closeFile();
}
},
{
label: 'New File',
accelerator: 'Ctrl+N',
click: () => {
console.log("This is not yet available.");
}
}
]
}))
mainTitlebar.updateMenu(menu);
</script>
<div class="grid-container">
<div class="buttons">
<div class="tooltip openFileContainer">
<button id="openFile" class="invisButton"><span class="mdi mdi-file mdi-48px"></span></button>
<div>File</div>
</div>
<div class="tooltip settingsContainer">
<button id="openSettings" class="invisButton"><span class="mdi mdi-cog-outline mdi-48px"></span></button>
<div>Settings</div>
</div>
<div class="tooltip closeProjectContainer">
<button id="closeFile" class="invisButton"><span class="mdi mdi-close mdi-48px"></span></button>
<div>Close Project</div>
</div>
</div>
<div class="projects">
<!-- <div class="project1">
<button class="invisButton activeProjectButton" id="project1Click">Project 1</button>
</div> -->
</div>
<div class="jsonEditor">
<!-- <div class="workSpace1 activeWorkSpace">
<h3>This is Workspace 1</h3>
</div> -->
<div id="jsonProjectContainer"></div>
<div class="unHidden">
<p id="welcomeMessage">Hello User! Welcome to Route. I hope you enjoy using this!</p>
</div>
<div id="updateNotifier" class="hidden">
<p id="updateAvailability"></p>
<button id="restartButton" onclick="restartApp()"></button>
</div>
</div>
<div class="sidebar">
<div class="tooltip tradeBuilder">
<button id="tradeSetup" class="invisButton"><span class="mdi mdi-apache-kafka mdi-48px"></span></button>
<div>Basic Trade</div>
</div>
<div class="tooltip whatNext">
<button id="whatsNext" class="invisButton"><span class="mdi mdi-help mdi-48px"></span></button>
<div>Whats Next?</div>
</div>
</div>
</div>
<script src="renderer.js"></script>
</body>
</html>
This is the error:
Uncaught (in promise) TypeError: Cannot read property 'min' of undefined
at createProject (renderer.js:126)
at renderer.js:48
And I am pretty sure the error comes from how I get "min". I have tried using ["min"], ['min'], .min, and as a last resort, [0].
I have the code hosted on github, if needing to be shared.
If you would debug your code, you would notice that the error occurs when j=1 and k=0. Then if you check the object, you notice that at that location there is no question property:
{
"tiers": [
{
"trades": [
{
/* ... */
},
{
"wants": [
{
"item": "minecraft:emerald"
/* no quantity here */
}
],
"gives": [
/* ... */
]
}
]
},
/* ... */
So you'll have to decide what you want to happen when there is no quantity property. You can for instance use the optional chaining operator to use a default value:
let wantsMin = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity.?min || 0;
let wantsMax = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity.?max || 0;
The above would give 0 as value for the min/max values that are missing. But all depends on what you want to happen in this scenario.
This is just an example on how you can circumvent the error, but might not be the right way to deal with it in light of the rest of your code. That is up to you to determine.
This code is to access or process the values that are in nested array.we are iterating with loops and checks the iterating values that either it is an array or not with .isArray if yes we fetch the value ,if no we return the value.
var arr=[['hi'],
['hello',['welcome','bye']],
['world']];
for(var i=0; i<arr.length; i++)
{
for(var j=0; j<arr.length; j++)
{
if(Array.isArray(arr[i][j]))
{
for(var k=0; k<arr.length; k++)
{
console.log(arr[i][j][k]);
}
}
else
{
console.log(arr[i][j]);
}
}
}
I have a BrowserView inside a BrowserWindow (I need both indeed):
const { app, BrowserWindow, BrowserView } = require('electron');
app.on('ready', () => {
browserWindow = new BrowserWindow({ width: 800, height: 500, frame: false });
bv = new BrowserView({ webPreferences: { nodeIntegration: false }});
bv.setBounds({ x: 0, y: 30, width: 800, height: 470});
bv.webContents.loadURL('https://old.reddit.com');
browserWindow.setBrowserView(bv);
});
Doing a right-click on web pages doesn't do anything. How to enable right-click to have "Back", "Forward", "Reload", "Copy", "Paste", etc. as usual with Chrome?
Electron has some sample menus up on their docs located at https://electronjs.org/docs/api/menu
// Importing this adds a right-click menu with 'Inspect Element' option
const remote = require('remote')
const Menu = remote.require('menu')
const MenuItem = remote.require('menu-item')
let rightClickPosition = null
const menu = new Menu()
const menuItem = new MenuItem({
label: 'Inspect Element',
click: () => {
remote.getCurrentWindow().inspectElement(rightClickPosition.x, rightClickPosition.y)
}
})
menu.append(menuItem)
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
rightClickPosition = {x: e.x, y: e.y}
menu.popup(remote.getCurrentWindow())
}, false)
Following this template you could set up custom roles like Back, Forward, Reload, etc. using custom javascript like this:
Back
const backMenuItem = new MenuItem({
label: 'Back',
click: () => {
window.history.back();
}
})
menu.append(backMenuItem)
Forward
const forwardMenuItem = new MenuItem({
label: 'Forward',
click: () => {
window.history.forward();
}
})
menu.append(forwardMenuItem)
I am using Angular 2 with Electron. Here I added printing option in Electron's menu, but actually I want that not to add print option in Electron's menu. I want to add it in each page of HTML templates.
import { app, BrowserWindow, screen, Menu, webContents,} from 'electron';
import * as path from 'path';
let win, serve;
let contents = webContents;
const args = process.argv.slice(1);
serve = args.some(val => val === '--serve');
if (serve) {
require('electron-reload')(__dirname, {
});
}
const template = [
// {
// label: 'Edit',
// submenu: [
// {role: 'undo'},
// {role: 'redo'},
// {role: 'cut'},
// {role: 'copy'},
// {role: 'paste'},
// {role: 'pasteandmatchstyle'},
// {role: 'delete'},
// {role: 'selectall'}
// ]
// },
{
label: 'View',
submenu: [
{role: 'reload'},
{role: 'forcereload'},
{role: 'toggledevtools'},
{role: 'resetzoom'},
{role: 'zoomin'},
{role: 'zoomout'},
{role: 'togglefullscreen'}
]
},
// {
// role: 'window',
// submenu: [
// {role: 'minimize'},
// {role: 'close'}
// ]
// },
// {
// role: 'help',
// submenu: [ ]
// },
{
***
label: 'Print',
click () { win.webContents.print({silent: false, printBackground: false, deviceName: ''})}
***
},
// {
// label:'Drugs',
// submenu: [
// {
// label: 'Resus Drugs',
// click () {win.webContents.canGoForward()}
// },
// {
// label: 'Intubation',
// click () { }
// },
// {
// label: 'Arrythmias',
// click () { }
// },
// {
// label: 'Infusion',
// click () { }
// },
// {
// label: 'Fluids',
// click () { }
// },
// ]
// },
]
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
function createWindow() {
const electronScreen = screen;
const size = electronScreen.getPrimaryDisplay().workAreaSize;
// Create the browser window.
win = new BrowserWindow({
// frame:false,
backgroundColor: '#97b714',
x: 0,
y: 0,
width: size.width,
height: size.height
});
// and load the index.html of the app.
win.loadURL('file://' + __dirname + '/index.html');
// Open the DevTools.
// if (serve) {
// win.webContents.openDevTools();
// }
// Emitted when the window is closed.
win.on('closed', () => {
// Dereference the window object, usually you would store window
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null;
});
}
try {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});
} catch (e) {
// Catch Error
// throw e;
}
I'm new to Electron and JavaScript. I'm building an Electron app. I know how to open a URL in a browser via clicking an item in native menu(by studying the documentation), but I need to open a html file in another Electron window using an Electron's native menu click. If I have my menu structure like below, how can I achieve this? Please help.
const {Menu} = require('electron');
const nativeMenus = [
{
label: 'About',
submenu: [
{
label: 'About',
click () {--- code to open about.html file in another electron window}
}
]
}
]
const menu = Menu.buildFromTemplate(nativeMenus);
Menu.setApplicationMenu(menu);
If it is all in the main.js just create a function to create a new window and then call that on menu item click.
const { Menu } = require('electron')
const ipc = require('electron').ipcRenderer
const nativeMenus = [
{
label: 'About',
submenu: [
{
label: 'About',
click() {
openAboutWindow()
}
}
]
}
]
const menu = Menu.buildFromTemplate(nativeMenus)
Menu.setApplicationMenu(menu)
var newWindow = null
function openAboutWindow() {
if (newWindow) {
newWindow.focus()
return
}
newWindow = new BrowserWindow({
height: 185,
resizable: false,
width: 270,
title: '',
minimizable: false,
fullscreenable: false
})
newWindow.loadURL('file://' + __dirname + '/views/about.html')
newWindow.on('closed', function() {
newWindow = null
})
}
Let me know if this works for you.
You stored your BrowserWindow instance in a variable, for the sake of this answer I am gonna suppose it's win. user7252292 did provide you with a good answer. But if you need another window then you will have to create another function for the same purpose. I will make a function for creating modals. They are basically windows that need a parent window and you cannot respond to the parent window until the modal is closed.
const createModal = (htmlFile, parentWindow, width, height) => {
let modal = new BrowserWindow({
width: width,
height: height,
modal: true,
parent: parentWindow,
webPreferences: {
nodeIntegration: true
}
})
modal.loadFile(htmlFile)
return modal;
}
const {Menu} = require('electron');
const nativeMenus = [
{
label: 'About',
submenu: [
{
label: 'About',
click () {
createModal("myfile.html",win,600,800); // Win is the browerwindow instance
}
}
]
}
]
const menu = Menu.buildFromTemplate(nativeMenus);
Menu.setApplicationMenu(menu);
Plus, you can store the createModal in a variable and modify the modal because it returns the modal itself.
I'm in the process of finishing up my first app on Electron, i'm a Junior Web Developer, so please excuse me if it's a simple mistake or you spot something you think can be done better. But basically, I'm building a basic menu on macOS so I can have Copy / Paste functionality after packaging. Now i've followed the documentation from https://github.com/electron/electron/blob/master/docs/api/menu.md, made a few tweaks to fit my needs but seem to be having a problem when running, the error being:
Uncaught Exception:
TypeError: Cannot read property 'buildFromTemplate' of undefined
at EventEmitter.createWindow (/Users/Jay/Desktop/click_palette_release/app/main.js:73:22)
at emitOne (events.js:101:20)
at EventEmitter.emit (events.js:188:7)
This to me would suggest that 'template' isn't defined? However I define it at the top on line 70 with const. What am I missing here? Been scratching my head at it for some time now.
const template = [
{
label: 'Edit',
submenu: [
{
role: 'copy'
},
{
role: 'paste'
},
]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
Line 73 being const menu = Menu.buildFromTemplate(template);
Thanks in advance!
Full code:
const electron = require('electron');
const {app} = electron;
const {BrowserWindow} = electron;
const Configstore = require('configstore');
const pkg = require(__dirname + '/init.json');
const sysconf = new Configstore(pkg.name);
var Menu = require("electron").menu;
let mainWindow;
function createWindow(){
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
if (sysconf.get('bounds')){
var bounds = sysconf.get('bounds');
} else {
var bounds = {'x':'', 'y':'', 'width':'400', 'height':'125'}
}
// Create the browser window.
mainWindow = new BrowserWindow({
x: bounds.x,
y: bounds.y,
width: 400,
height: 90,
//'titleBarStyle': 'hidden',
title: 'ClickPalette',
backgroundColor: '#fff',
frame: false
});
mainWindow.setResizable(false);
mainWindow.setAlwaysOnTop(true);
mainWindow.loadURL('file://' + __dirname + '/index.html');
mainWindow.setMenu(null);
//mainWindow.webContents.openDevTools();
mainWindow.on('close', function(e) {
var bounds = mainWindow.getBounds();
sysconf.set({'bounds' : bounds});
});
// Emitted when the window is closed.
mainWindow.on('closed', function() {
mainWindow = null;
});
//Settings / Manager
var manageWindow = new BrowserWindow({
width: 400,
height: 400,
show: false
});
manageWindow.loadURL('file://' + __dirname + '/manage.html');
//manageWindow.webContents.openDevTools();
const template = [
{
label: 'Edit',
submenu: [
{
role: 'copy'
},
{
role: 'paste'
},
]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
};
// Load mainWindow
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') {
app.quit();
}
});
//Show window
app.on('activate', function (e) {
if (mainWindow === null) {
createWindow();
}
});
RESOLVED
I was using const Menu = require('electron').menu; but the M in menu should have been uppercase: const Menu = require('electron').Menu;