23
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ElectronAdvent Calendar 2017

Day 25

Angular on Electron

Last updated at Posted at 2017-12-25

Angular on Electron

初めまして、私が@jialipassionです、初めてQiitaで投稿して、宜しくお願いします。
今までhttps://github.com/angular/zone.js でいろいろContributeしていますが、今日はElectronでAngularの開発でいくつヒントを共用します。

  • Angular on Electron
  • Electron Native API (NgZoneを利用する)
  • 新しいzone-patch-electronの使い方(自動Patch)

Angular on Electron

ElectronでAngularの開発は普通のElectronの開発とあまり差がありません。
https://github.com/maximegris/angular-electron をベースに直接やってもいいですが、自分で@angular/cliでやっても問題ありません。

1. 環境構築

npm install @angular/cli
ng new electron-angular
cd electron-angular
npm install --save-dev electron electron-reload

そして、Electronのエントリのmain.tsを用意します。

import { app, BrowserWindow, screen } from 'electron';
import * as path from 'path';

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

if (serve) {
  require('electron-reload')(__dirname, {
  });
}

function createWindow() {

  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
  });

  // and load the index.html of the app.
  win.loadURL('file://' + __dirname + '/index.html');

  // Open the DevTools.
  if (serve) {
    win.webContents.openDevTools();
  }

  // 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.
    win = null;
  });
}

try {

  // 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;
}

これが普通のElectronのエントリとあまり変わりません。

2. zone-mix適用

Angularでzone.jsを利用して、ChangeDetectionを探知しています。@angular/cliでのsrc/polyfill.tsで下記のコードがあります。

import 'zone.js/dist/zone';  // Included with Angular CLI.

これがBrowser用のzone.jsをロードしました。でもElectronでBrowserとNodeJs両方のAPIがあって、Browserだけであれば、NodeJSのfsとかEventEmitterなどでzoneに対応されていません。たとえば、AngularのComponentで

fs.readFile(..., (err, data) => {
   // not in ngZone
   // will not auto update DOM
   this.content = data;
})

のようなコードで、自動てきにAngularのChangeDetectionを実行しません。
なので、Electronの場合、上記のBrowser zoneをBrowser+NodeJSのzoneを切り替える必要があります。
src/polyfill.tsで下記のように切り替えます。

//import 'zone.js/dist/zone';  // Included with Angular CLI.
import 'zone.js/dist/zone-mix';  // Included with Angular CLI.

Electron Native API

上記の対応で、NodeJSの基本のAPIが対応されました、でもElectronのAPIがまだ対応されていません。
たとえば、ElectronのMenuItemとか、DesktopCapturerとか、これらの非同期処理するとき、CallbackがNgZoneではないので、DOMが更新していません。たとえば、AngularのComponentで下記のMenuを初期化したら、

app.component.ts

const template = [{
  label: 'Edit',
  submenu: [
    {
      label: 'submenu',
      click: () => {
        // not in ngZone
    // title will not be updated in DOM
        this.title = 'menuclicked';
      }
    },
  ]
}];
const menu = this.electron.remote.Menu.buildFromTemplate(template);
this.electron.remote.Menu.setApplicationMenu(menu);

TitleがDOMに更新されていません。

このとき、ngZone.runが必要です。

app.component.ts

constructor(private ngZone: NgZone) {...}

ngOnInit() {
  const template = [{
    label: 'Edit', 
    submenu: [
      {
        label: 'submenu',
        click: () => {
          this.ngZone.run(() => {
             this.title = 'menuclicked';
          });
        }
      },
    ]
  }];
  const menu = this.electron.remote.Menu.buildFromTemplate(template);
  this.electron.remote.Menu.setApplicationMenu(menu);
}

新しいzone-patch-electron

上記のNgZoneでElectron Native APIを対応できますが、NgZoneを意識してちょっと面倒かもしれませんが、
zone.jsで今新しいzone-patch-electronを提供しました、(まだリリースしていないですが。。。)
https://github.com/angular/zone.js/pull/983

これがリリースしたら、ngZone.runがいらなくなります。
使い方が、src/polyfill.ts

import 'zone.js/dist/zone';  // Included with Angular CLI.
import 'zone.js/dist/zone-patch-electron';  // Add this line.

を追加したら、自動的に大部のElectron APIを自動てきにPatchされます。ちょっとまだリリースしていないですが、
次のバージョンをおまちください。

以上!

どうもありがとうございました!

23
27
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
23
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?