はじめに
electron-vueでWindows/macOS向けのアプリを作成することがあると思います
デスクトップ向けOSのアプリで欲しい機能といえば、おそらく 「ファイル(ディレクトリ)を開くダイアログ」 ではないでしょうか? (少なくとも私はそうでした)
そんなときに、dialogを使用しようとすると出る厄介なエラーとその解決方法を記載します
※前提として、Typescriptで一部のコードが記載されています
元のコードと発生するエラー
元のコード
MyComponent.vue
<template>
<div class="mycomponent">
<button @click="OpenDialog" />
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { remote } from 'electron
@Component
export default class AppCard extends Vue {
private OpenDialog() {
remote.dialog.showOpenDialog({defaultPath: '/', properties: ['openFile']})
}
}
</script>
src/background.ts
// :
// :
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule: true,
}
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}
// :
// :
発生するエラー
Uncaught ReferenceError: __dirname is not defined
解決方法
// src/background.ts
// :
// :
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule: true,
+ nodeIntegration: true
}
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}
// :
// :
解決した結果
一難去ってまた一難とはこのことだと感じます
さらに修正します
- preloadの項目をwebPreferenceに追加し
- preload.jsを対象のプロジェクトに追加し
- preload.jsを記述し
- 実際に
showOpenDialog
を呼び出す箇所を変更します
1. preloadの項目をwebPreferenceに追加
// src/background.ts
// :
// :
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
enableRemoteModule: true,
+ preload: path.join(__dirname, '..', 'src', 'preload.js'),
nodeIntegration: true
}
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}
// :
// :
2. preload.js をプロジェクトに追加 (パスは先ほど追加したコードの位置と合わせる必要があります)
3. preload.jsを記述
preload.js
const remote = require("electron").remote;
window.remote = remote;
4. 実際に showOpenDialog
を呼び出す箇所を変更
<!-- MyComponent.vue -->
<template>
<div class="mycomponent">
<button @click="OpenDialog" />
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { remote } from 'electron
@Component
export default class AppCard extends Vue {
private OpenDialog() {
- remote.dialog.showOpenDialog({defaultPath: '/', properties: ['openFile']})
+ window.remote.dialog.showOpenDialog({defaultPath: '/', properties: ['openFile']})
}
}
</script>
最終コード
src/background.ts
// :
// :
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
enableRemoteModule: true,
preload: path.join(__dirname, '..', 'src', 'preload.js'),
nodeIntegration: true
}
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}
// :
// :
preload.js
const remote = require("electron").remote;
window.remote = remote;
MyComponent.vue
<template>
<div class="mycomponent">
<button @click="OpenDialog" />
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { remote } from 'electron
@Component
export default class AppCard extends Vue {
private OpenDialog() {
window.remote.dialog.showOpenDialog({defaultPath: '/', properties: ['openFile']})
}
}
</script>
ただし
以下のように、tslintによって赤波線が立ちます
ので、windowに依存させない方法をとった方が良いかもしれません