LoginSignup
1
0

More than 3 years have passed since last update.

electron-vue で dialog.showOpenDialog(...) しようとした時に出る厄介なエラーと対処法

Posted at

はじめに

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

スクリーンショット 2021-02-11 19.02.59.png

解決方法

// 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')
  }
}
// :
// :

解決した結果

一難去ってまた一難とはこのことだと感じます

スクリーンショット 2021-02-11 19.02.14.png

さらに修正します

  1. preloadの項目をwebPreferenceに追加し
  2. preload.jsを対象のプロジェクトに追加し
  3. preload.jsを記述し
  4. 実際に 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 をプロジェクトに追加 (パスは先ほど追加したコードの位置と合わせる必要があります)

スクリーンショット 2021-02-11 19.20.17.png

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に依存させない方法をとった方が良いかもしれません

スクリーンショット 2021-02-11 19.27.56.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0