How do I use require('electron') in a quasar vue component environment? - vue.js

How do I access the main electron process fs module from within a renderer side module like a vue component running within the Quasar framework.
I've tried a few variations in a component with the following error:
const { app } = require('electron')
vue-router.esm.js?8c4f:1897 TypeError: fs.existsSync is not a function
const { app } = window.require('electron')
TypeError: window.require is not a function
After looking at what I could find through my friend Google, I am still searching for an answer on how to access the electron main process functions from within a vue component running under the quasar framework. Anyone... anyone? I've seen some github examples of file explorers, but based on the electron documentation it seems the implementation of just simply calling something like fs.readdirSync() should be a lot simpler than what I'm seeing in those implementations.

Your problem is explained in the Quasar docs
https://quasar.dev/quasar-cli/developing-electron-apps/node-integration
Quasar's suggestion is to use a preload script to attach the node APIs that you want in your renderer processes (ie: BrowserWindows) to the global window object.
https://quasar.dev/quasar-cli/developing-electron-apps/electron-preload-script
Attach preload script to BrowserWindow (Main Process)
src-electron/electron-main.js:
import path from 'path'
win = new BrowserWindow({
...
webPreferences: {
preload: path.resolve(__dirname, 'electron-preload.js')
}
})
Attach Node APIs to window global (Preload Script)
src-electron/electron-preload.js:
window.electron = require('electron')
Use Node API through the window global (Renderer Process)
somefile.vue
window.electron.ipcRenderer.sendSync(
'message',
payload
)

The answer was just beyond my understanding of how all these components are working together. Hopefully this will help someone else just coming up to speed on developing a Quasar/Vue/Electron app. If you launch your app/website using
quasar dev
you get a browser (renderer) that communicates with main electron process that cannot handle node main process stuff like:
const electron = require('electron')
const fs = require('fs')
const files = fs.readdirSync('/')
console.log(files)
I couldn't find a clear, concise and simple way. It appears there is a webpack config that can provide the same 'deep' integration, but I was looking for a more out of the box solution.
If you launch your app
quasar dev -m electron
You get deep integration and now can 'require()' or import the above modules within Vue components in your Quasar app.

const electron = require('electron')

Related

Running html only if running in Electron.js [duplicate]

I'm trying to serve real react app on electron app. It doesn't mean I'm developing electron app with react. I've created a react app and injected it into electron app. (Like slack, it will serve as a web application and desktop application.) But I'm confused that send desktop notifications.
Now the main question is:
How can I get the application type. I mean, is user using my app on web or on desktop. How can I get this?
Thank you :)
There are many ways to detect whether you are running in a desktop environment or not.
You can check the User-Agent and you can set the userAgent value in Electron when you call loadURL.
Another way is declaring a global variable using a preload script.
// main process
new BrowserWindow({
webPreferences: {
preload: "preload.js",
},
});
// preload.js
// you don't need to use contextBridge if contextIsolation is false
// but it's true by default in Electron 12
const { contextBridge } = require("electron");
contextBridge.exposeInMainWorld("IN_DESKTOP_ENV", true);
// renderer process (your React world)
if (globalThis.IN_DESKTOP_ENV) {
// do something...
}

How debug Vue3 applications with chrome and native javascript debugger

i started a new application with vue3 (created with vue-cli) and I'm not able to exploit native debugger in chrome
I have read that I had to specify the source map in my vue.config.js
module.exports = {
pluginOptions: {
quasar: {
importStrategy: 'kebab',
rtlSupport: false
}
},
configureWebpack: {
devtool: 'source-map'
},
transpileDependencies: [
'quasar'
]
}
But I'm still not able to explore my component code with a debugger
the result I have
screenshot not working
and what i want (screen from a vue2 project)
screenshot working
To use the native js debugger in a vue 2 application. You can do something like this:
methods: {
doSomething () {
this.loading = true
// doing something
debugger // native js debugger, in console check => this.loading (= true)
this.loading = false
}
}
Hopefully, it works the same way in vue 3.
You may be tempted to use it in the life-cycle hooks such as mounted, created ... but unfortunately, that never worked for me. Once the debugger halts the program, you can test it in the console by seeing what kind of object this identifies as.
When the native js debugger is used in a method enclosed by the methods option, it acts in a helpful and expected way. However, when it is used in a lifecycle hook like created the this object is not what you would expect it to be.
Additionals:
I actually stumbled on this question because I was looking for ways to use the native js debugger in the life cycle hooks. I'm hoping there might be vue 2 life-cycle hooks that support it.

VueJS and ElectronJS | VueJS stops rendering completely when I import ipcRenderer in a component

So I am coding a VueJS and ElectronJS template which can be found here: https://github.com/dev-aethex/electronjstemplate
My code works something like this,
Inside of my Vue component I access a global pre constructed class called MainProcessInterface and when it's constructed it first checks if vue is compiled for running in a development server. If it's in a dev server it will connect to the dev socket which electrons main process will host if electron is in dev mode and not compiled. This method seems to be working great, I had to use a socket because vue dev server is being loaded into electron via loadURL and so vue has no clue what ipcRenderer is. Inside the main process interface, if vue is compiled it will instead use the ipcRenderer.send() method. This is were the problem was born.
As soon as Vue runs thought the TS code, it sees ipcRenderer.send and freaks out while printing an error to the electron window console saying fs.existsSync does not exist or is defined.
I can't find a way around this. I though maybe i'll split MainProcessInterface into 2 peices, one for ipc and the other for websockets. Although it isn't a very good way, so before implementing it, I would like to know if there is a better more proper way of doing such.
I had a similar issue with React. Are you importing the ipcRenderer object somewhere in your build process? You might want to make sure it references the correct variable. I tried to drop this in as a comment but it wouldn't fit:
//index.html (index.ejs) for me... This is in the main HTML entry point
var IPC = null;
try {
IPC = require('electron').ipcRenderer;
console.log('IPC IS: ' + IPC)
} catch (err) {
console.log('CRICITCAL ERROR: IPC NOT ENABLED')
console.log(err)
IPC = null;
}
Then I initialize off that context in React with a startup here:
setTimeout(()=>{
console.log('----------------HACK FIRED POST REHYDRATE')
window.REDUX_STORE.dispatch(
(dispatch, getState) => {
const _state = getState()
if(window.IPC) {
if(_state.osc && _state.osc.on) {
dispatch( reconnectToEos() )
} else {
dispatch( updateStatus('[OSC Startup: DISCONNECTED]', ))
}
console.log('\t------------ELECTRON')
} else {
//Shut off OSC
dispatch( updateOscKey('on', false) )
dispatch( updateStatus('[WebApp, OSC disabled]', ))
console.log('\t------------WEB')
}
}
)
}, 1000)
Basically I'm using a global variable (window.IPC) to initialize my app so I don't import a bad variable in my build process. I have a fair number of Electron APIs where this alleviates the issues with building via Webpack.
I hope this helps!

How to refer other html file to load in production mode of application that are developed using electron?

I have problem in building electron application, which is made using electron.js and vue.js.
The development is almost done, so I need to build it and make production.
Basically, my project has license system, so when the program starts, users should activate it by entering license key. I wrote this part inside activate.html. So, inside public folder, there are two html files, one is default index.html, and the other is activate.html
Inside activate.html, when activation is success, startMain() javascript function is called. Inside this function, the program is navigated to main program, especially to index.html. This html file is Vue application, and then main program is started.
All this process is working fine in the development mode. But when it
comes to production, this is not working anymore. I probably know why this
happens, this is just because index.html is not available in
production mode when all these are built up.
Alternatively, I tried to load localhost:8080 inside
startMain(). But also this works fine in development mode, but unluckily, we don't have localhost server on production mode, so this
doesn't solve the problem.
This is startMain() inside activate.html
function startMain() {
window.__static = "index.html";
// Alternatively, we can use location.href on development mode.
// window.location.href = "http://localhost:8080/";
}
This is background.js ( electron main engine)
function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 1400,
height: 900,
titleBarStyle: "hiddenInset"
});
win.setMenuBarVisibility(false);
if (isDevelopment) {
// Load the url of the dev server if in development mode
// win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
win.loadURL(`file://${process.cwd()}/public/activate.html`);
// if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol("app");
// Load the index.html when not in development
win.loadFile("activate.html");
}
win.on("closed", () => {
win = null;
});
}
What I want is to make it run correctly in production mode.
Specifically, when the user finishes activation flow ( activate.html),
it should start main program ( index.html )
Please help me with this issue. I welcome any comments.
I built this application using vue-cli-plugin-electron-builder package
In Vue usually you use vue-router to switch between views. You set the BrowserWindow.location the first time, in background.js, but after that you are in the Vue app and you make router calls like this:
router.replace({ name: "index" }).catch(() => { });
It's hard to guess what to do in your case, without seeing the rest of your activate.html. You need to define router in your App and probably want to convert your activate.html into an activate.vue component and make it the proper first page of your App.
I think the cli-plugin boilerplate will call main.js where you will define your app and bootstraps it. You add router there:
import router from "./router";
window.$app = new Vue({
router,
In your bootstrap function you can also call something like router.replace({ name: "activate"}) to start your app with your activate vue component.

Passing data between Vue siblings

After researching this question thoroughly and after implementing the Bus method to communicate between siblings, I am getting a webpack error, so either i'm
not implementing right (using latest CLI as of yesterday), or I need some other method.
I am new to Vue, coming from React and I have built a simple app wrapping 2 components in app.vue:
an input that sends data:
<GoogleInput #searchEvent="getSearchLocation($event)"></GoogleInput>
and a map container that should receive the data:
<GoogleMapsContainer :location="location" name="location-project"></GoogleMapsContainer>
I successfully implemented child (GoogleInput) to parent communication in app.js via:
getSearchLocation(input) {
this.location = input;
}
with a method in GoogleInput:
this.$emit('searchEvent', ev.target.value);
Up to here everything was smooth.
Yet when trying to communicate my input value through to sibling (GoogleMapsContainer) via the bus method:
In my entry index.js: const Bus = new Vue({});
A new emit in my sending component:
Bus.$emit('passLocation', {location: this.location})
and in my receiving component:
Bus.$on('passLocation', (input) => {
this.location = input;
});
I get a webpack error:
Error in created hook: "TypeError: __WEBPACK_IMPORTED_MODULE_1_vue__.$on is not a function"
I am looking for "the shortest distance" to communicate my input to map container, without going into solving webpack issues (if this is a webpack issue at all, or just a fat finger mistake).
BTW: If vuex isn't a time consuming method to implement (as React-Redux is) in here, that
would be a cool route as well, but I must keep this design (already in
Git)
Thanks
You probably have some import errors.
In your index.js, export it like:
export const Bus = new Vue({});
And in the files you do Bus.$emit(...) or Bus.$on(...) import it like:
import { Bus } from './index'; // make sure you use the correct relative path