How to get folder path using electron - javascript

I am very new to the electron. Can anyone suggest me how to get a local folder's relative path using the electron? JavaScript does not have that capability.
I have a Choose File button(see snapshot), so my question is that when I select a folder and click on the open button then it should return a whole directory path.

As #phuongle pointed out in the comments you want to use showOpenDialog(). Something like this:
var remote = require('remote');
var dialog = remote.require('electron').dialog;
var path = dialog.showOpenDialog({
properties: ['openDirectory']
});
UPDATE: If the above isn't working for your current Electron version, you should try more modern importing:
const {dialog} = require('electron').remote;
In addition, in order to use remote, you need to set enableRemoteModule when creating your window in your main process:
const myWindow = new BrowserWindow({
webPreferences: {
enableRemoteModule: true
}
});

Following the official IPC tutorial worked for me
main process:
import {dialog, ipcMain} from 'electron'
function createWindow () {
mainWindow = new BrowserWindow({/*Your electron window boilerplate*/})
ipcMain.handle('dialog:openDirectory', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory']
})
if (canceled) {
return
} else {
return filePaths[0]
}
})
}
preload script:
import {contextBridge, ipcRenderer} from 'electron'
contextBridge.exposeInMainWorld('myAPI', {
selectFolder: () => ipcRenderer.invoke('dialog:openDirectory')
})
Now you can call the selectFolder method from your application code and get the user input.
window.myAPI.selectFolder().then(result=>{/* Do something with the folder path*/})

In Electron we can select the directory by specifying simple input element with type="file" and webkitdirectory attribute'.
<input id="myFile" type="file" webkitdirectory /> and we can get the directory full path with the path property of File object document.getElementById("myFile").files[0].path

You would use Node's path.relative for that.

The solution for me was simply using all in lowercase, with a value true as string in my react component. No extra configuration was required.
Like so:
<input
id="path-picker"
type="file"
webkitdirectory="true"
/>
Edit
It turns out that, as mentioned by #cbartondock, it will recursively look for files in the directory, which is not good!
I ended up using the required electron remote's dialog.

Related

How to make multiple Svelte windows in Electron?

For my electron app, I would like to open another Svelte-window (or load different windows/components depending on the startup variables).
So let's say I used this tutorial to set up a basic structure, with my App.svelte looking like this:
<script>
const openLauncher = () => {
window.api.openWindow("launcher");
};
</script>
<button on:click={openLauncher}>Open Launcher</button>
As you can see, I added an IPC function to open a new window. The corresponding index.js:
const { app, BrowserWindow, ipcMain } = require("electron");
const { join } = require("path");
app.on("ready", () => {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: join(__dirname, "./preload.js"),
}
});
mainWindow.loadFile(join(__dirname, "../public/index.html"));
});
ipcMain.on("openLauncher", (event, args) => {
const launcher = new BrowserWindow({
webPreferences: {
preload: join(__dirname, "./preload.js"),
}
});
launcher.loadFile(join(__dirname, "../public/launcher.html"));
});
preload.js:
const { contextBridge, ipcRenderer } = require("electron");
const API = {
openWindow: (obj) => ipcRenderer.send("openLauncher", obj),
}
contextBridge.exposeInMainWorld("api", API);
This does work and opens a new window with the launcher.html, but I can't figure out how to get the Svelte components working in that new file.
One idea I had was modifying main.js file so that the body component changes, like so:
import App from './App.svelte';
import LauncherApp from './LauncherApp.svelte';
const bodyID = document.getElementsByTagName('body')[0].id;
const app = {};
if (bodyID == "index") {
app = new App({
target: document.body,
});
}
else if (bodyID == "launcher") {
app = new LauncherApp({
target: document.body,
});
}
export default app;
This works for the main window (i.e. if I switch the IDs, it loads the correct component at startup) but since it doesn't load any Svelte whe opening the new window, this doesn't work.
So I would really appreciate any ideas on how to get Svelte to load for new/different windows/html-files! And if there is a way to do this with SvelteKit, even better!
Thank you in advance!
The obvious quick fix I see is using an #if block on bodyID in App.svelte containing two components, MainApp (content of App.svelte) and LauncherApp, and then simply changing bodyID depending on in which mode you are.
When using sveltekit I think it would make sense to treat LauncherApp as a separate route (I believe this is the only way to have "separated" pages with sveltekit, though I am not 100%). So when opening a new window you navigate the new instance of your application to the LauncherApp route. If you don't want the same base layout as in the main app, you can add a __layout.reset.svelte file.
I don't know why your solution didn't work, it was quite elegant.
As this post helped me alot to create seperate windows using Svelte and Electron, i just had to create my account after lurking Stack Overflow for years. I think i've cracked your problem without the "hackery" workaround.
I got it working without the #IF statements in my original App.svelte. This is how i did it:
My main.js in the src map (renderer side) is as follows:
import App from './App.svelte';
import Window2 from './Window2.svelte';
let bodyID = document.getElementsByTagName('body')[0].id;
let app;
if (bodyID == "index"){
app = new App({
target: document.body,
});
}
else if (bodyID == "window2"){
app = new Window2({
target: document.body,
});
}
export default app;
I think however that the real magic happens in my index.html and Window2.html. I made mine using the excellent YouTube videos provided by Tylerlaceby.
Basicly, the index.js in the main folder (so the Electron main js) opens the window with the following lines:
const main_window = new BrowserWindow({//Your settings
});
main_window.loadFile(join(__dirname, "../public/index.html"));
main_window.on("ready-to-show", main_window.show);
And the index.html that is in the public folder contains the following head:
`
<title>Main Window</title>
<link rel='icon' type='image/png' href='./favicon.ico'>
<link rel='stylesheet' href='global.css'>
<link rel='stylesheet' href='build/bundle.css'>
<script defer src='build/bundle.js'></script>
The body is empty, but has the id attached which i use in the main.js renderer side script.
I believe that the header in the index.html loads the build folder after the electron app has been build, containing all of your converted Svelte files.
The Window2.html is the same. The secondary window loads it the same way as the main window does in the second code block from the main.js on the electron side but refers to the Window2.html. The Window2.html contains a body with the ID Window2.
If the above is not the solution, it could also be because i use let instead of const. Once a variable has been assigned to const, it cannot be changed which might explain why it works the first time, but not the second time.
I'm sorry if i did not get all the formatting and refering to previous awnsers correctly. I'm still using to learn Stackoverflow but was eager to share how i made it work for me.

Send ipcRenderer variable to vue3 page

I have a vue3 app with electron and need to send a variable from the ipcRenderer to my Vue3 page. I cant figure out how to do it especially given vue strips away lots of js. What im trying to do is to save the path of a folder which so far works fine, then display it in the vue3 app either in a span or whatever. I successfully got the value i need to display to the ipcRenderer but cant access it using my vue app.
Vue3 page
<q-btn
id="showpath"
dark
flat
size="xl"
label="show Path"
type="submit"
#click="showpath"
/>
</div>
export default {
name: "Settings",
props: {},
methods: {
loader() {
window.postMessage({
type: "select-dirs",
});
},
showpath() {
const testa = window.postMessage({ type: "pathtf"})
console.log("Vue page says :"+ testa)
},
},
};
</script>
All I get here is "undefined
Preloader script
const { ipcRenderer } = require('electron');
const settings = require('./settings');
process.once('loaded', () => {
window.addEventListener('message', evt => {
if (evt.data.type === 'select-dirs') {
ipcRenderer.send('select-dirs')
}
}),
window.addEventListener('message', evt => {
if (evt.data.type === 'pathtf') {
const pathtf = settings.gettfpath("pathtf")
console.log(pathtf)
}
})
})
The console.log in the preload file works and displays the value, but i cant get that value to my vue3 page.
Any tip? Thank you
I would suggest that you use electron's contextBridge to expose certain methods (send/receive) to the renderer process.
I do not know what your gettfPath method does but if that variable is available to you in the preloadjs file you should be able to expose it as a variable like so:
const {contextBridge} = require("electron");
contextBridge.exposeInMainWorld("electronApi", {
pathtf: settings.gettfPath()
});
With this your path will be exposed to your renderer process as window.electronApi.pathtf
Here are two resources you may find helpful:
How to use preload.js properly in Electron
https://medium.com/swlh/how-to-safely-set-up-an-electron-app-with-vue-and-webpack-556fb491b83 (This one may not be exactly what you are looking for but it has a good explanation / example of how to use ipc with view and electron)

Equivalent to directory, mozdirectory and webkitdirectory in react?

I want to upload directories to my server using react and I tried using directory, mozdirectory, and webKitDirectory attributes but they didn't work and I tried to run the code that is below but unfortunately, it didn't work.
function UploadDirectory() {
function dirUploadAttr(input) {
input.setAttribute("webkit-directory", "");
input.setAttribute("moz-directory", "");
}
return (
<input type="file" ref={dirUploadAttr} />
)
}
How do I take directories in the input tag in react?
What is the effective and simple way to do it?
I'll be honest, me and a team member at the corporation I've worked at have looked into this issue for several weeks. The issue being that the users we were making our app for wanted a way to strip a file from a directory and upload it without having to double click in the directory itself to obtain the file (Ex. Click the folder containing the file in the upload menu and selecting upload to upload whole folder). Well, after digging and digging we got into contact with multiple teams in order to find a solution and they told us this: "webkitdirectory (or anything like it for that matter) is not supported in React and that's due to a Windows limitation." I believe it could have been done in the .NET era but React's specific <input)> html tag doesn't support webkitdirectory as an attribute. We instead incorporated a dropzone from the node module react-dropzone-uploader:
import Dropzone from "react-dropzone-uploader";
import { getDroppedOrSelectedFiles } from './Html5FileSelector'
const getFilesFromEvent = (e:any) => {
return new Promise<any>(resolve => {
getDroppedOrSelectedFiles(e).then(chosenFiles => {
resolve(chosenFiles.map((f:any) => f.fileObject))
})
})
};
const handleSubmit = (files:any) => { this.fileChange(files); }
<Dropzone accept=".txt" getFilesFromEvent={getFilesFromEvent} onSubmit=
{handleSubmit} />
Note that you will have to implement a function from Html5FileSelector, more information can be obtain from the live example showcasing the dropzone uploader, this is also a link to the official documentation of the node module:
https://react-dropzone-uploader.js.org/docs/examples#!/Custom%20Input,%20Directory%20Drag%20and%20Drop

Loading A HTML From Another Directory In Electron

I am trying to make an electron app and I need to load a HTML to a BrowserWindow but the script that controls this is in another Directory which isn't the same as the HTML's ones.
This is how it looks:
This is my code to load the HTML into the BrowserWindow:
const electron = require("electron");
const {BrowserWindow} = electron;
//This part is called from an exported function
const window = new BrowserWindow();
window.setSize(500 , 500);
window.show();
window.setMenu(null);
window.loadFile("../HTML/MainWindow.html");
The window is created but it doesn't load the HTML into it which is really weird, someone help? By the way keep in mind that I don't create the window from the main script.
Try with this
var path = require('path');
window.loadURL(path.join(__dirname, '../HTML/MainWindow.html'))
So I managed to solve the problem by importing path like what #Luis Daniel Sandi talled me to do and then I just did: window.loadURL(path.join(__dirname, '../HTML/MainWindow.html')) and instead of importing BrowserWindow from electron I imported that from electron.remote.

How to select file on fs with react and electron?

I'm building desktop application with Electron and ReactJS.
I need to implement feature to select file from file system, like input="file" works in forms.
Actually, all I need is to obtain absolute path of the file.
How can I achieve that?
I tried:
<input type="file" onChange={function(e) {console.log(e.target.value)} } />
But it returns fakepath due to security reasons.
I think Dialogs in Electron may be useful for this purpose, but how to propagate file path to react application then?
const {dialog} = require('electron').remote;
...
document.querySelector('#fileSelect').addEventListener('click', function (event) {
dialog.showOpenDialog({
properties: ['openFile', 'multiSelections']
}, function (files) {
if (files !== undefined) {
// handle files
}
})
});

Categories

Resources