从角度组件关闭电子应用程序

Posted

技术标签:

【中文标题】从角度组件关闭电子应用程序【英文标题】:Close electron app from angular component 【发布时间】:2021-05-10 22:39:37 【问题描述】:

我正在尝试弄清楚如何关闭带有角度组件的电子应用程序。我通过在 BrowserWindow(... inside main.js 中设置 frame: false 来删除菜单栏。我在电子应用程序的右上角有一个来自组件的关闭按钮。我希望能够从点击时的component.ts文件,但我还没有看到任何从角度组件关闭电子的示例。

我认为以下方法会起作用,但没有。我正在从 main.js 导出一个函数并从组件中调用该函数。 像这样(见closeApp()):

const  app, BrowserWindow  = require('electron')
const url = require("url");
const path = require("path");

let mainWindow

function createWindow() 
  mainWindow = new BrowserWindow(
    width: 800,
    height: 600,
    frame: false,
    webPreferences: 
      nodeIntegration: true
    
  )
  mainWindow.maximize();
  mainWindow.loadURL(
    url.format(
      pathname: path.join(__dirname, `/dist/index.html`),
      protocol: "file:",
      slashes: true
    )
  );

...

function closeApp() 
  mainWindow = null


export  closeApp ;

然后我会尝试将其导入到组件中

import  Component, OnInit  from '@angular/core';
import  closeApp  from '../../../main.js';

@Component(
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss']
)
export class HeaderComponent implements OnInit 

  constructor()  

  ngOnInit(): void 

  
  testClose() 
    closeApp();
  


如何从角度关闭电子应用程序?感谢您的帮助!

【问题讨论】:

I get build errors with electron ...你能分享这些错误吗? 更正:我确实有构建错误,但它们无关。构建错误现在已经消失,所以这不是问题。 我认为这个问题的答案仍然适用:***.com/questions/43314039/… 如果我尝试这个解决方案:***.com/a/43314199/4350389 然后我得到编译器错误。这是该解决方案中我更新的更改和编译器错误。 pastebin.com/yXLqaKXz你知道如何解决这些错误吗? 不能使用IPC调用吗?我通常有一个 Angular 服务来完成与 Node 进程的所有通信。您可以查看this article 以获得更多解释 【参考方案1】:

您可以在浏览器窗口中使用这样的代码。请记住,它需要enableRemoteModule,出于安全原因不建议这样做。你可以用更复杂的代码来解决这个问题,但这应该会告诉你基本的想法

更简单但不太安全的方法

在你的主进程中:

mainWindow = new BrowserWindow(
    width: 800,
    height: 600,
    frame: false,
    fullscreen: false,
    webPreferences: 
      nodeIntegration: true,
      enableRemoteModule: true
    
  )

来自您的mainWindow,在渲染器中

点击您应用的关闭按钮(您的自定义按钮)后,您应该让它运行以下逻辑:

const win = remote.getCurrentWindow();
win.close();

https://www.electronjs.org/docs/api/browser-window#winclose

win.destroy() 也可用,但应首选win.close()


更难但更安全的方法

这需要使用预加载脚本这是另一个问题,但我将包含这样做的代码,但由于多种原因很难开始工作。

也就是说,从长远来看,这是您实现事物的目标。

  import  browserWindow, ipcMain  from 'electron';

  mainWindow = new BrowserWindow(
    width: 800,
    height: 600,
    frame: false,
    fullscreen: false,
    preload: [Absolute file path to your preload file],
    webPreferences: 
  );

  ipcMain.handle('close-main-window', async (evt) => 
    mainWindow.close();
    // You can also return values from here

  );


preload.js

  const  ipcRenderer  = require('electron');

  // We only pass closeWindow to be used in the renderer page, rather than passing the entire ipcRenderer, which is a security issue
  const closeWindow = async () => 
     await ipcRenderer.invoke('close-main-window'); // We don't actually need async/await here, but if you were returning a value from `ipcRenderer.invoke` then you would need async/await
  

  window.api = 
    closeWindow
  

渲染器页面

  
// I don't know Angular, but just call window.api.closeWindow() in response to the button you want to use

【讨论】:

我是否应该像const remote = require('electron').remote; 这样声明远程以便使用我在我的角度组件顶部添加这一行,我收到一堆错误:postimg.cc/fSbG0t9D。也许我正在定义remote 错误你还说有一种更安全的更好方法。我正在构建一些东西,我希望有一天它会成为一个成熟的产品,所以安全性是一个优先事项。如果我的问题是如何做到这一点,我无法解决它,哈哈。您能否提供有关从组件中关闭应用程序的更安全方法的更多详细信息? 您还需要在创建窗口时启用nodeIntegration。请参阅electronjs.org/docs/api/browser-window#new-browserwindowoptions - 正如您所提到的,如果您的问题是“如何做”,您将无法解决它。 -- 更安全的替代方法是使用ipcRenderer.invokeipcMain.handle,但与上面的代码相比,它相当复杂。我建议使用上面的代码,一旦你理解了它,你就可以使用 IPC 而不是 remote 来实现它。 我在我的组件中尝试了如下代码:pastebin.com/Xi7vrH64,但出现错误:postimg.cc/rzHNVJKr。这是我在 component.ts 文件中使用的代码:pastebin.com/Xi7vrH64 为了解决这个问题,我搜索了一下,似乎我可以将"types": ["node"] 添加到 tsconfig.json 或 tsconfig.app.json 或两者并运行npm install @types/node,然后我得到Module not found: Error: Can't resolve 'fs' in 'C:\Users\Phil\Documents\... 我在我的代码的骨架版本中也尝试了更改:github.com/user6680/angular-ipc,但同样的问题。随意使用 repo 代码尝试一下,让我知道你的想法。 我将代码更改为添加 enableRemoteModule: true ,但是当我在角度组件中取消注释 const remote = require('electron').remote; 时,它仍然会引发该错误。参考:postimg.cc/rR0njm3S 和 postimg.cc/rKGCkx1D。如果我注释掉该行 const remote = require('electron').remote;const win = remote.getCurrentWindow(); win.close(); 则不会发生错误【参考方案2】:

将此代码添加到您的 main.ts 文件中

mainWindow.on('closed', () => 
    // Dereference the window object, usually you would store window
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    
    mainWindow = null;
    
  );

return mainWindow;

mainWindow.on("Closed") 将捕获十字图标上的单击事件,届时我们将返回将关闭窗口的 win null。

我的 main.ts 文件和它的功能。

import  app, BrowserWindow, screen, Menu  from 'electron';
import * as path from 'path';
import * as url from 'url';
import * as electronLocalshortcut from 'electron-localshortcut'

let win: BrowserWindow = null;
const args = process.argv.slice(1),
  serve = args.some(val => val === '--serve');

function createWindow(): BrowserWindow 

  const electronScreen = screen;
  const size = electronScreen.getPrimaryDisplay().workAreaSize;

  // Create the browser window.
  win = new BrowserWindow(
    x: 0,
    y: 0,
    width: size.width,
    height: size.height,
    icon: path.join(__dirname, 'dist/assets/logo.png'),
    webPreferences: 
      nodeIntegration: true,
      allowRunningInsecureContent: (serve) ? true : false,
    ,
  );


  // Disable refresh
  win.on('focus', (event) => 
    console.log("event of on fucous ");
    electronLocalshortcut.register(win, ['CommandOrControl+R', 'CommandOrControl+Shift+R', 'F5'], () =>  )
  )

  win.on('blur', (event) => 
    console.log("event of on blue ");
    electronLocalshortcut.unregisterAll(win)
  )
  if (serve) 
    require('electron-reload')(__dirname, 
      electron: require(`$__dirname/node_modules/electron`)
    );
    win.loadURL('http://localhost:4200');
   else 
    win.loadURL(url.format(
      pathname: path.join(__dirname, 'dist/index.html'),
      protocol: 'file:',
      slashes: true
    ));
  

  if (serve) 
    win.webContents.openDevTools();
  

  win.on('close', (e) => 
    // Do your control here

    e.preventDefault();

  );
  // Emitted when the window is closed.
  win.on('closed', () => 
    // Dereference the window object, usually you would store window
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    // if (JSON.parse(localStorage.getItem('isRunning'))) 
    //   alert('Your timer is running')
    //  else 
    win = null;
    // 
  );


  return win;


try 

  // Custom menu.
  const isMac = process.platform === 'darwin'

  const template: any = [
    //  role: 'fileMenu' 
    
      label: 'File',
      submenu: [
        isMac ?  role: 'close'  :  role: 'quit' 
      ]
    ,
    //  role: 'editMenu' 
    
      label: 'Window',
      submenu: [
         role: 'minimize' ,
      ]
    
  ]

  const menu = Menu.buildFromTemplate(template)
  Menu.setApplicationMenu(menu)

  // This method will be called when Electron has finished
  // initialization and is ready to create browser windows.
  // Some APIs can only be used after this event occurs.
  app.on('ready', createWindow);

  // Quit when all windows are closed.
  app.on('window-all-closed', () => 
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') 
      app.quit();
    
  );

  app.on('activate', () => 
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (win === null) 
      createWindow();
    
  );

 catch (e) 
  // Catch Error
  // throw e;

谢谢。

【讨论】:

你如何声明win?如果您查看我的 main.ts github.com/user6680/angular-ipc/blob/master/main.ts,您会发现它尚未定义,因此我不确定您是如何定义它的。目前mainWindow 是我相信的窗口对象,这就是我将其设置为null 的原因。另外,我正在使用 ipc 尝试从角度组件发送关闭命令,因此 frame 在该 BrowserWindow(... 对象内设置为 false ,因此被单击的按钮来自角度而不是框架。 将 win 替换为 mainWindow。我的错。 @user6680 这是我的 main.ts 文件的样子。我正在更新我的答案。 @user6680 我很欣赏这次更新,但有两点。我更新了我的 main.ts 文件以使用您编辑的 main.ts 文件,但我得到了postimg.cc/wyX24Cwb。此外,它没有回答我关于如何从角度组件关闭电子应用程序的问题。如果这个运行没有那个错误,我相信它只会通过电子框架而不是角度组件关闭应用程序。最好按照我最初的问题,我想看看它是如何通过 IPC 角度服务完成的。这是我创建的一个示例存储库,用于演示我通过 Angular 服务 github.com/user6680/angular-ipc 的 IPC 尝试执行的操作 @user6680 你试过我的答案了吗?如果您尝试一下,它将向您展示如何解决您的问题。通过IPC 实现它的唯一区别是您必须编写更多代码来做同样的事情。如果您理解我分享的简单代码,那么您将有 95% 的方法使用 IPC 来解决它。【参考方案3】:

这可能是也可能不是最好的方法,但它似乎是一种解决方法: 为了捕捉这个事件,我使用了“window.onbeforeunload”。

例如:

export class AppComponent implements OnInit 

  
  ngOnInit(): void    

    window.onbeforeunload = (e: BeforeUnloadEvent) => 
     // Do something...
    
  

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于从角度组件关闭电子应用程序的主要内容,如果未能解决你的问题,请参考以下文章

可以在角度调用父组件功能时单击子组件上的任何按钮? [关闭]

我有角度组件的问题[关闭]

编写全局函数以在角度的所有组件中使用

从服务传递到组件的角度错误消息

从角度应用程序访问角度库 - 角度组件中的 StaticInjectorError 与构造函数中的 ElementRef

以角度将数据从一个组件传递到另一个组件