Angular on Electron

# 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. 環境構築

```shell
npm install @angular/cli
ng new electron-angular
cd electron-angular
npm install --save-dev electron electron-reload
```
そして､Electronのエントリの`main.ts`を用意します｡

```javascript
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`で下記のコードがあります｡

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

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

```javascript
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`で下記のように切り替えます｡

```javascript
//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

```javascript
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

```javascript
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`で

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

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

以上!

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