How to use Electron API with Vue js component? - vue.js

I'm using the electron-vue boilerplate, and I want to make my mainWindow a fullScreen after clicking a button.
Electron Window API: electron.atom.io/docs/api/browser-window
HTML:
<button #click="setFullScreen">Switch to Full Screen</button>
Vue:
export default {
name: 'mainComponent',
methods: {
setFullScreen: function() {
mainWindow.setFullScreen(true);
}
}
This is not working. How can I use the Electron API in electron-vue?
and the index.js:
'use strict'
import { app, BrowserWindow } from 'electron'
let mainWindow
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:${require('../../../config').port}`
: `file://${__dirname}/index.html`
function createWindow () {
/**
* Initial window options
*/
mainWindow = new BrowserWindow({
height: 728,
width: 1024,
fullscreen: false,
})
mainWindow.loadURL(winURL)
mainWindow.on('closed', () => {
mainWindow = null
})
// eslint-disable-next-line no-console
console.log('mainWindow opened')
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
you will find it as it is in electron-Vue
the picture shows How the Structure of the folder
enter image description here

mainWindow is not available in your Vue code because it is defined in your main process.
In your single file component, however, you can import remote from electron, where you can get access to the current window. So your code would look something like this.
const {remote} = require("electron")
export default {
name: 'mainComponent',
methods: {
setFullScreen: function() {
remote.getCurrentWindow().setFullScreen(true);
}
}
}

Related

Vue 3 - How to execute a function on binging instance in custom directive

Im creating a custom directive as follows in main.ts .
let handleOutsideClick: any;
app.directive("closable", {
mounted: (el, binding, vnode) => {
handleOutsideClick = (e: any) => {
e.stopPropagation();
const payload = binding.value;
console.log(`instance: ${Object.getOwnPropertyNames(binding.instance)}`);
};
document.addEventListener("click", handleOutsideClick);
},
unmounted: (el) => {
document.removeEventListener("click", handleOutsideClick);
},
});
Inside the event handler i want to make a call to a function on the component that triggered this directive.
With Vue 2 you could do it with vnode.context'myfunction' but this does not seem to work with binding.instance.
How can i call the function using the binding instance?
Passing the function to be called as the binding's value and then calling it seems to work:
if (typeof binding.value === 'function') {
binding.value()
}
Working example:
const { createApp } = Vue;
const app = createApp({
setup() {
return { test: () => console.log('here') }
}
})
app.component('demo', {
template: `<div>test</div>`
})
let handleOutsideClick;
app.directive("closable", {
mounted: (el, binding, vnode) => {
handleOutsideClick = (e) => {
e.stopPropagation();
const payload = binding.value;
console.log(`instance: ${Object.getOwnPropertyNames(binding.instance)}`);
if (typeof binding.value === 'function') {
binding.value()
}
};
document.addEventListener("click", handleOutsideClick);
},
beforeUnmount: (el) => {
document.removeEventListener("click", handleOutsideClick);
},
});
app.mount('#app')
<script src="https://unpkg.com/vue#3.2.41/dist/vue.global.prod.js"></script>
<div id="app">
<demo v-closable="test"></demo>
</div>
Notes:
vue internal method names change based on environment. For example, if you remove .prod from the vue link in the example above, you'll get more data out of Object.getOwnPropertyNames(binding.instance).
If your app's business logic relies on vue internals method naming:
  a) you're doing it wrong™;
  b) it won't work in production
if the above is not helpful, please provide more details on your specific use-case via a runnable minimal, reproducible example.
If you need a multi-file node-like online editor, note codesandbox.io allows importing local projects using their CLI utility.

Nuxt Loader - Throttle for Custom Loader

I'm using a custom loader component for my project, and my nuxt config looks like this:
loading: '~/components/common/loading.vue'
The problem is that this component doesn't throttle a few milli-seconds and with every page change, this flickers and causes a bad user experience. Is there any way to add a throttle as we'd normally add for the default component like throttle: 200 inside the loading object like,
loading: { throttle: 200 }
Since my loading option doesn't have an object, instead has a string/path to my custom loading component, I'm not sure what to do here.
Reference: https://nuxtjs.org/docs/2.x/features/loading
This is how I use a custom loading component using Vuetify overlay component with a throttle:
<template>
<v-overlay :value="loading">
<v-progress-circular
indeterminate
size="64"
/>
</v-overlay>
</template>
<script>
export default {
data: () => ({
loading: false
}),
methods: {
clear () {
clearTimeout(this._throttle)
},
start () {
this.clear()
this._throttle = setTimeout(() => {
this.loading = true
}, 200)
},
finish () {
this.clear()
this.loading = false
}
}
}
</script>
This is inspired by the Nuxt default loading component.
You could add a setTimeout within your start() method in your custom loader component ~/components/common/loading.vue.
methods: {
start() {
setTimeout(() => {
this.loading = true;
}, 2000);
},
finish() { ... }
}

How pass data from component to external js file

I want to use my component's data within my external JavaScript file, containing my Dropzone configuration. I tried unsuccessfully to use Function.prototype.bind:
export const dropzoneConfig = {
url: api.http.baseUrl + '/files',
thumbnailWidth: 150,
maxFilesize: 5,
acceptedFiles: 'image/*',
addRemoveLinks: true,
sending: function (file, xhr, formData) {
formData.append('type', 'photo');
},
success: function (file, xhr) {
file.id = xhr.data.id;
if (this.entry.files === undefined) {
this.entry.files = [];
}
this.entry.files.push(xhr.data);
this.saveNote();
}.bind(this),
headers: api.http.authHeaders().headers
};
In the code above, this.entry and this.saveNote are unavailable because they're from my Vue component. How do I make them accessible to the external file?
A more general solution would be for the component to pass in a success-event handler that has access to the component's data and methods, as shown below. This solution decouples the configuration from the component's internals.
dropzoneConfig.js:
export const dropzoneConfig = ({ onSuccess }) => ({
//...
success(file, xhr) {
//...
onSuccess(xhr.data)
}
})
App.vue:
<script>
import Dropzone from 'dropzone'
import { dropzoneConfig } from './dropzoneConfig'
export default {
data() {
return {
entry: {
files: []
}
}
},
created() {
Dropzone.options.myComponent = dropzoneConfig({
onSuccess: fileData => this.onDropzoneSuccess(fileData)
})
},
methods: {
saveNote() {
//...
},
onDropzoneSuccess(fileData) {
this.entry.files.push(fileData)
this.saveNote()
}
}
}
</script>

Show updated PWA created in Vue

I uasing Vue.js with Vuetify and creating a PWA.
I have service-worker.js in /public folder
A snippet from vue.config.js:
pwa: {
// configure the workbox plugin
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// swSrc is required in InjectManifest mode.
swSrc: 'public/service-worker.js',
// ...other Workbox options...
}
}
This looks too be working good and caching the shell etc.
I run build and serve up the project
npm run build
The problem i have is when i update any files, i can't see the updated changes.
when i navigate to the url in my android device the page remains as the old one (cached).
How can i get it to update?
I tried including this code in index.html, but no success:
https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users
service-worker.js
importScripts("/precache-manifest.8812c20b1b3401cbe039782d13cce03d.js", "https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
console.log(`Hello from service worker`);
if (workbox) {
console.log(`Workbox is loaded`);
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
skipWaiting();
}
});
}
else {
console.log(`Workbox didn't load`);
}
Not sure what exactly your setup setup is, but it should be similar. Using the #vue/cli-plugin-pwa and with minimal setup below.
This will show a dialog when a new version of your app is available. Clicking yes will update your app. You will have to refresh the page somehow to actually show the new version, but that is up to you on how solve that.
vue.config.js:
module.exports = {
pwa: {
name: "name-of-your-app",
short_name: "noya",
themeColor: "#000000",
workboxPluginMode: "InjectManifest",
workboxOptions: {
swSrc: "src/service-worker.js" // CHECK CORRECT PATH!
}
}
};
src/main.js:
import Vue from "vue";
import App from "./App.vue";
import "./registerServiceWorker";
// whatever other imports...
new Vue({
render: h => h(App)
}).$mount("#app");
src/registerServiceWorker.js:
import { register } from "register-service-worker";
if (process.env.NODE_ENV === "production") {
register(`${process.env.BASE_URL}service-worker.js`, {
updated(registration) {
if (window.confirm("A new version is available, update now?")) {
const worker = registration.waiting;
worker.postMessage({ action: "SKIP_WAITING" });
// refresh the page or trigger a refresh programatically!
}
}
});
}
src/service-worker.js:
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
self.addEventListener("message", (event) => {
if (event.data.action == "SKIP_WAITING") self.skipWaiting();
});
I get it work by following the offer_a_page_reload_for_users. The original registerServiceWorker.js seems redundant though.
src/registerServiceWorker.js
import { Workbox } from "workbox-window";
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
const wb = new Workbox("/service-worker.js");
wb.addEventListener("waiting", () => {
const result = window.confirm("refresh now?");
if (result) {
wb.messageSW({ type: "SKIP_WAITING" });
}
});
wb.addEventListener("controlling", () => {
window.location.reload();
});
wb.register();
}

how to fix blank page if i am using vue router with electron js?

I'm trying to use vue router with an application on an Electron JS. If I use a router on the render page, then the router work done. But I do not understand how to make the transition to the page, for example,- 'Settings' using the Tray. At attempt of transition the empty page opens.
I have prepared a working example of the project. This problem exists only build project. In development mode all work well.
This is my work example on github. Please need help.
git clone https://github.com/DmtryJS/electron-vue-example.git
cd electron-vue-example
npm install
npm run build
and run dist\win-unpacked\example_for_stackoverflow.exe
my main.js file
'use strict'
import { app, protocol, BrowserWindow, Menu, ipcMain, Tray } from 'electron'
import { format as formatUrl } from 'url'
const electron = require('electron');
const path = require('path');
const isDevelopment = process.env.NODE_ENV !== 'production';
let imgBasePath;
if(isDevelopment) {
imgBasePath = path.join('src','assets', 'img');
} else {
imgBasePath = path.join(path.dirname(__dirname), 'extraResources', 'img');
}
let win;
let tray;
protocol.registerStandardSchemes(['app'], { secure: true })
const trayIcon = path.join(__static, 'img', 'icon.png');
function createWindow () {
win = new BrowserWindow({
width: 800,
height: 600,
icon: trayIcon
})
routeTo(win, "")
win.on('closed', () => {
win = null
})
//убрать меню
win.setMenuBarVisibility(true)
win.on('show', function() {
tray.setHighlightMode('always')
})
win.on('hide', function() {
tray.setHighlightMode('never')
})
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (win === null) {
createWindow()
}
})
app.on('ready', () => {
createWindow()
win.webContents.openDevTools(); //открыть dev tools
createTray()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', data => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
function createTray()
{
let traiIconPath = path.join(imgBasePath, 'preloader_tray_icon.png')
tray = new Tray(traiIconPath)
const contextMenu = Menu.buildFromTemplate(
[
{
label: 'Settings',
type: 'normal',
click: function()
{
routeTo(win, "/settings")
let contents = win.webContents
contents.on('dom-ready', function()
{
if(!win.isVisible())
{
showWindow()
}
})
}
},
{
label: 'Exit',
type: 'normal',
click: function()
{
win = null
app.quit()
}
}
])
tray.setContextMenu(contextMenu)
tray.on('click', function() {
toggleWindow();
})
}
function showWindow() {
var position = getPosition();
win.setPosition(position.x, position.y, false)
win.show()
win.focus()
}
ipcMain.on('routerEvent', function(event, arg) {
routeTo(win, arg)
})
function routeTo(win, to) {
if (isDevelopment) {
win.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` + to)
} else {
win.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html' + to);,
protocol: 'file',
slashes: true
}))
}
}
And
router.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from './../views/Main.vue'
import Settings from './../views/Settings.vue'
Vue.use(Router)
export default new Router({
//mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Main
},
{
path: '/settings',
name: 'settings',
component: Settings
}
]
})
You need to add created to the main Vue app check docs
// src/main.js
new Vue({
router,
render: h => h(App),
created() {
// Prevent blank screen in Electron builds
this.$router.push('/')
}
}).$mount('#app')
The solution for me was to remove the history mode in the vue router.
Sorry, but after one day of googling, I just found a solution. The case turned out to be
win.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html' + to);,
protocol: 'file',
slashes: true
}))
I delete formaUrl and everything works well
win.loadURL(path.join(__dirname, 'index.html' + to));
For me solution was this:
Check if app is running at addresses like this:
Local: http://x86_64-apple-darwin13.4.0:8080/
Network: http://localhost:8080/
Check if you can access x86_64..url in browser. If you are not seeing a webpage but can see it from localhost, then map 127.0.0.1 to x86_64-apple-darwin13.4.0 in hosts file. in mac its located in /etc/hosts in windows its in system32/drivers/etc.