【问题标题】:Electron - Uncaught TypeError: Cannot read properties of undefined (reading 'showOpenDialog')电子 - 未捕获的类型错误:无法读取未定义的属性(读取“showOpenDialog”)
【发布时间】:2022-05-05 14:21:12
【问题描述】:

我正在尝试打开电子对话框,但出现此错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'showOpenDialog')

我已经访问了多个论坛和社区,看看是否有解决问题的方法。但没有一个解决可能是因为版本。

我目前使用的electron版本是16.0.5

这个答案对我没有多大帮助

https://stackoverflow.com/a/63756725/14271847

我不会留下我所有的 main.js,但我改变的部分是这个,enableRemoteModule

  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      enableRemoteModule:true,
      nodeIntegration: true,
      contextIsolation: false,
      preload: path.join(__dirname, 'preload.js')
    }
  })

文件 test.js

这就是我想要在电子上做的事情,遵循 link 中的内容

const { dialog } = require('electron')
console.log(dialog.showOpenDialog({ properties: ['openFile', 'multiSelections'] }))

我用遥控器试过了,还是不行:

const { dialog } = require('electron')
console.log(dialog.remote.showOpenDialog({ properties: ['openFile', 'multiSelections'] }))

有人可以帮忙吗?

【问题讨论】:

    标签: javascript node.js electron dialog


    【解决方案1】:

    启用 Context Isolation 并禁用 nodeIntegration 是当今真正保护您的 Electron 应用程序的最佳实践。此外,不鼓励使用remote,因为我们现在有Inter-Process Communication

    不知道您的 preload.js 脚本是什么样的,我在下面包含了一个简单的 preload.js 脚​​本,它使用了白名单频道名称列表和仅实现 ipcRenderer 方法。也就是说,具有不同外观的 preload.js 脚本不应该改变太多应该使用的核心方法 你的main.jsindex.html 文件让你的对话正常工作。


    使用invoke 方法将允许我们向主进程发送一条消息以打开对话框接收回复,一切合二为一。

    preload.js(主进程)

    // Import the necessary Electron components.
    const contextBridge = require('electron').contextBridge;
    const ipcRenderer = require('electron').ipcRenderer;
    
    // White-listed channels.
    const ipc = {
        'render': {
            // From render to main.
            'send': [],
            // From main to render.
            'receive': [],
            // From render to main and back again.
            'sendReceive': [
                'dialog:openMultiFileSelect' // Channel name
            ]
        }
    };
    
    // Exposed protected methods in the render process.
    contextBridge.exposeInMainWorld(
        // Allowed 'ipcRenderer' methods.
        'ipcRender', {
            // From render to main.
            send: (channel, args) => {
                let validChannels = ipc.render.send;
                if (validChannels.includes(channel)) {
                    ipcRenderer.send(channel, args);
                }
            },
            // From main to render.
            receive: (channel, listener) => {
                let validChannels = ipc.render.receive;
                if (validChannels.includes(channel)) {
                    // Deliberately strip event as it includes `sender`.
                    ipcRenderer.on(channel, (event, ...args) => listener(...args));
                }
            },
            // From render to main and back again.
            invoke: (channel, args) => {
                let validChannels = ipc.render.sendReceive;
                if (validChannels.includes(channel)) {
                    return ipcRenderer.invoke(channel, args);
                }
            }
        }
    );
    

    以上preload.js脚本的使用总结如下。

    /**
     * Render --> Main
     * ---------------
     * Render:  window.ipcRender.send('channel', data); // Data is optional.
     * Main:    electronIpcMain.on('channel', (event, data) => { methodName(data); })
     *
     * Main --> Render
     * ---------------
     * Main:    windowName.webContents.send('channel', data); // Data is optional.
     * Render:  window.ipcRender.receive('channel', (data) => { methodName(data); });
     *
     * Render --> Main (Value) --> Render
     * ----------------------------------
     * Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
     * Main:    electronIpcMain.handle('channel', (event, data) => { return someMethod(data); });
     *
     * Render --> Main (Promise) --> Render
     * ------------------------------------
     * Render:  window.ipcRender.invoke('channel', data).then((result) => { methodName(result); });
     * Main:    electronIpcMain.handle('channel', async (event, data) => {
     *              return await promiseName(data)
     *                  .then(() => { return result; })
     *          });
     */
    

    main.js 脚本中,使用handle 方法,让我们监听dialog:openMultiFileSelect 频道上的消息。收到后,打开dialog.showOpenDialog() 方法并.then 等待结果。收到结果后(IE:选择文件并接受对话框),将结果(通过handle 方法)返回给渲染进程。

    main.js(主进程)

    const electronApp = require('electron').app;
    const electronBrowserWindow = require('electron').BrowserWindow;
    const electronDialog = require('electron').dialog;
    const electronIpcMain = require('electron').ipcMain;
    
    const nodePath = require("path");
    
    // Prevent garbage collection
    let window;
    
    function createWindow() {
        const window = new electronBrowserWindow({
            x: 0,
            y: 0,
            width: 800,
            height: 600,
            show: false,
            webPreferences: {
                nodeIntegration: false,
                contextIsolation: true,
                preload: nodePath.join(__dirname, 'preload.js')
            }
        });
    
        window.loadFile('index.html')
            .then(() => { window.show(); });
    
        return window;
    }
    
    electronApp.on('ready', () => {
        window = createWindow();
    });
    
    electronApp.on('window-all-closed', () => {
        if (process.platform !== 'darwin') {
            electronApp.quit();
        }
    });
    
    electronApp.on('activate', () => {
        if (electronBrowserWindow.getAllWindows().length === 0) {
            createWindow();
        }
    });
    
    // ---
    
    electronIpcMain.handle('dialog:openMultiFileSelect', () => {
        let options = {
            properties: ['openFile', 'multiSelections']
        };
    
        return electronDialog.showOpenDialog(window, options)
            .then((result) => {
                // Bail early if user cancelled dialog
                if (result.canceled) { return }
    
                return result.filePaths;
            })
    })
    

    最后,在index.html 文件中,我们监听按钮上的click 事件,然后通过dialog:openMultiFileSelect 通道发送消息以打开对话框(在主进程中)。返回结果后,.then 将显示所选文件。

    请注意我们如何检测undefined 的结果并进行相应的管理。当用户取消对话框时会发生这种情况。

    index.html(渲染进程)

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Electron Test</title>
            <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
        </head>
    
        <body>
            <input type="button" id="button" value="Open File Dialog">
    
            <ul id="paths"></ul>
        </body>
    
        <script>
            document.getElementById('button').addEventListener('click', () => {
                window.ipcRender.invoke('dialog:openMultiFileSelect')
                    .then((paths) => {
                        if (paths === undefined) { return } // Dialog was cancelled
    
                        let result = '';
    
                        for (let path of paths) {
                            result += '<li>' + path + '</li>';
                        }
    
                        document.getElementById('paths').innerHTML = result;
                    })
            })
        </script>
    </html>
    

    【讨论】:

      猜你喜欢
      • 2021-12-22
      • 2021-12-25
      • 2021-11-24
      • 2021-10-31
      • 2021-11-07
      • 2022-01-17
      • 2023-03-13
      • 2022-01-01
      • 2022-01-10
      相关资源
      最近更新 更多