Aurelia に Quill を導入する

Aurelia を使った社内向けシステムの開発中に Quill 導入したので、手順とサンプルを載せてみる。

# [Quill](https://quilljs.com/) とは
> Your powerful, rich text editor.

要するに WYSIWYG エディタ。
見た目がモダンっぽくて良い感じなので、これを Aurelia アプリに組み込む。

# TL;DR
- Quill はモダンで導入も楽チンだよ
- 簡単な設置までの手順を書いたよ
- ちょっと実用的な「記事」の表示・編集サンプルも書いたよ

# 対象読者
Aurelia に Quill の導入を考えている人。

# 前提環境
- [Aurelia](http://aurelia.io/) 1.1.2
- [jspm](https://jspm.io/) 0.16.53

# 導入するもの
- [Quill](https://quilljs.com/) 1.3.2

# 導入手順
## jspm を使ってインストール
入れるのは一つだけで良いから楽チン。

```console
$ jspm install npm:quill
```

`package.json` に以下の内容が追記されればインストール完了。

```json:package.json
{
  "jspm": {
    "dependencies": {
      "quill": "npm:quill@^1.3.2",
    }
  }
}
```

## Aurelia に組み込む
Aurelia Plugin ではないので `main.js` に設定は要らない。

まずは html 側に css を読み込んで Editor を組み込ませるタグを作る。
専用のタグなどは不要で、後で Javascript 側でセレクタ指定できれば良い。今回は `div#editor` としておく。

```html:editor.html
<require from="quill/dist/quill.snow.css"></require>

<div id="editor"></div>
```

次に Javascript 側に処理を組み込む。
DOM が生成されてから組み込みたいので `attached()` が適当。

```js:editor.js
import Quill from 'quill/dist/quill.min.js';

export class Editor {
  attached() {
    this.quill = new Quill('#editor', {
      theme: 'snow',
    });
  }
}
```

画面を開いて、以下のように表示されれば設置完了。

![ 2017-09-24 20.14.01.png](https://qiita-image-store.s3.amazonaws.com/0/89904/e8ad1783-b76a-32b7-8461-09c7535fa846.png)

Quill はテーマ設定ができて、上記の例では `snow` を適用している。
他に `bubble` というものがあるので、変えたければ `snow` を全部 `bubble` に置き換えれば良い。

他にもオプション設定は多様にできるので、詳細は公式の [Configuration](https://quilljs.com/docs/configuration/) とかを見れば良いと思う。

# もう少し実用的に作ってみる
設置するだけなら以上。
さすがにこれだと実用性皆無なので、もう少し実用性のあるっぽい「記事」の表示・編集を考えて作ってみる。

新規投稿と編集の画面は似通っていると思うので、以下では編集画面だけ作る。
新規投稿画面を作りたければ、編集画面を手直しすれば良いよ。

なお、簡単に見栄えを整えるために Bootstrap をしれっと使用しているので、留意してね。

## ざっくりディレクトリ構成
- article
  - index.html
  - index.js
  - edit.html
  - edit.js

## ざっくりテーブル
- article
  - id: int(11)
  - title: varchar(255)
  - body: text

## 実装
### index：投稿された記事を表示する

html 側は複数件の表示に対応。
`ql-container` クラスなどを使って要素を用意しておけば、エディターと同等の見栄えを確保できる。

```html:index.html
<require from="quill/dist/quill.snow.css"></require>

<div class="card" repeat.for="article of articles">
  <div class="card-block">
    <h2 class="card-title">${article.title}</h2>
    <p class="card-text">
      <div class="ql-container ql-snow">
        <div class="ql-editor" innerhtml.bind="article.body"></div>
      </div>
    </p>
    <a href="article/edit/${article.id}" class="btn btn-primary" for="articleEdit">Edit</a>
  </div>
</div>
```

Javascript 側は取得するだけ。
API アクセスは https://github.com/SpoonX/aurelia-api を使ってます。

```js:index.js
import {inject} from "aurelia-framework";
import {Endpoint} from 'aurelia-api';

@inject(Endpoint.of('api'))

export class Index {
  constructor(endpoint) {
    this.endpoint = endpoint;
  }

  activate() {
    this.endpoint.find('article')
    .then(response => {
      this.articles = response;
    })
    .catch(error => {
    });
  }
```

画面を開いてこうなれば完了。
データは適当に入れてある前提。

![ 2017-09-24 21.08.24.png](https://qiita-image-store.s3.amazonaws.com/0/89904/13f446b5-7a1c-e559-342d-6acc1ae11b91.png)

### edit：記事を編集する
html 側はこんな感じ。
body は bind できないので Javascript 側で対応する。

```html:edit.html
<require from="quill/dist/quill.snow.css"></require>

<div class="form-group">
  <label for="articleTitle">Title</label>
  <input type="text" class="form-control" placeholder="title" value.bind="article.title">
</div>
<div class="form-group">
  <label for="articleBody">Body</label>
  <div id="article-body"></div>
</div>
<div class="form-group">
  <button type="button" class="btn btn-primary" for="articleSubmit" click.trigger="submit()">Submit</button>
</div>
```

Javascript 側はこんな感じ。
body は bind できていないので `quill.root.innerHTML` を介して設定・取得を行う。

```js:edit.js
import {inject} from "aurelia-framework";
import {Endpoint} from 'aurelia-api';
import Quill from 'quill/dist/quill.min.js';

@inject(Endpoint.of('api'))

export class Edit {
  constructor(endpoint) {
    this.endpoint = endpoint;
  }

  attached() {
    this.quill = new Quill('#article-body', {
      theme: 'snow',
      placeholder: 'body',
    });
  }

  activate(params) {
    this.endpoint.find('article/' + params.id)
    .then(response => {
      this.article = response;
      this.quill.root.innerHTML = this.article.body;
    })
    .catch(error => {
    });
  }

  submit() {
    let params = {
      article: {
        title: this.article.title,
        body: this.quill.root.innerHTML,
      }
    };

    this.endpoint.update('article', this.article.id, params)
    .then((response) => {
      this.successSubmit();
    })
    .catch(error => {
    });
  }

  successSubmit() {
    location.href = 'article/index';
  }
}
```

画面を開いてこうなれば完了。

![ 2017-09-24 21.10.14.png](https://qiita-image-store.s3.amazonaws.com/0/89904/ed199e50-73bf-6dc1-e809-8a88619a94dd.png)

# おわりに

設置は簡単で、必要なスタイルは css を読み込めば終わるので、導入コストが低くて楽チン。
テーマとして `snow` と `bubble` があって、見た目がモダンっぽくて、ちょっとオシャレなのは好印象だった。
とりあえず WYSIWYG を入れるにはもってこいって感じ。

ただ、やっぱり外部プラグインで実現すると js でお世話しないといけない辛さがある。
innerHTML プロパティとか書かないといけないので、 Aurelia でやっている感が薄れてしまうのは嫌かも。

Aurelia Plugin に寄せて template 呼び出し的なことができれば、もう少しシンプルに書けるかもしれない。
けど、それはまたいずれ。