summernote on laravel の導入備忘録

### インストール(npm)
`npm install summernote`でnode_modulesに入る
レイアウトがいろいろ用意されている。
通常は`require('summernote');`で行けるが
日本語化とレイアウトをbootstrap4ベースにしたかったけどサンプルなかった。
多分以下で行けるはず。

```javascript:resources/js/bootstrap.js
require('summernote/dist/summernote-bs4.min');
require('summernote/dist/lang/summernote-ja-JP.min');
```
```sass:resources/js/app.scss
@import 'summernote/dist/summernote-bs4.min';
```

### フロント側(bladeファイルとjavascript)
divタグを用意するだけ。textareaタグでもよい。

```html:edit.blade.php
<div id="summernote"></div>
<textarea id="summernote" class="form-control" name="description"></textarea>
```

```javascript:edit.js
$(document).ready(function()
{
  $('#summernote').summernote({
    lang: 'ja-JP',
    height: 300,
  });
})
```

### 画像挿入時のサーバ転送
デフォルトはimgタグにbase64エンコードされたデータとして挿入されます。
独自のサーバにアップロードしてそのURLをimgタグに挿入する方法も用意されている。

```javascript
  $('#summernote').summernote({
...略...
    callbacks: {
      onImageUpload: (files) => {
        /* ローカルファイルを選んだあと呼ばれるのでサーバーへアップする */
        insertImageFile(files[0]);
      },
      onMediaDelete: (target) => {
      /* 画像を削除するを選んだ時呼ばれるのでサーバーへ削除要求する */
        removeImageFile(target[0].src);
      },  
    },
...略...

function insertImageFile(file)
{
  let formData = new FormData();
  formData.append('file', file);
  
  fetch('/insert-image-file', formData,...)
  .then((response) => {
    if (response.ok) {
      // サーバ側で応答に画像のurlを付与する
      let imgNode = document.createElement('img');
      imgNode.src = response.url;
      $('#summernote').summernote('insertNode', imgNode);
    }
  });
}

function removeImageFile(src)
{
  /* 削除のアクションを呼ぶ */
  let formData = new FormData();
  formData.append('src', src);
  fetch('/remove-image-file', formdata, ...)
  .then((response) => {
    if (response.ok) {
    }
  }  
}
```

サーバ側(laravel/php)ではinsert-image-fileとremove-image-fileに対応する
アクションを用意する。

```php:app/Http/Controller/TestController.php

public function insertImageFile(Request $request)
{
  /* ファイル名をuuidに変換 */
  $uuid = (string) Str::uuid();
  $ext = $request->file('file')->extension();
  $file = $uuid . '.' . $ext;
  $imgpath = $request->file('file')->storeAs('attachments', $file, 'public');
  $url = Storage::disk('public')->url($imgpath);
  return response()->json(['result' => 'OK', 'url' => $url]);
}

public function removeImageFile(Request $request)
{
  /* storageまでのパスを取得 */
  $url = Storage::disk('public')->url('');
  /* 画像のパス(URL)からサーバ内のローカルパスに変換 */
  $delete_file = str_replace($url, '', $request->src);
  $resp = Storage::disk('public')->delete($delete_file);
  return response()->json(['result' => 'OK']);
}
```

### フォームのsubmitアクション
HTMLエディタの入力欄に対応するinputタグがないので
submitのクリックイベントを引っ掛けて隠し属性のinputを追加してsumbmitする
buttonタグにname="submit"があるとformのsubmitが効かないので削除する。
追記(2020/12/15)
普通にtextareaタグにid設定すればsummernoteのレイアウトになるっぽいので
以下の処理は必要ない。

```javascript
  $("#submit_button").click((e) => {
    /* 一旦submitを無効 */
    e.preventDefault();
    let description = $('#summernote').summernote('code');
    let edit_form = document.getElementById('edit_form');
    if (edit_form != null) {
      inputDescription = document.createElement("input");
      inputDescription.type = 'hidden';
      inputDescription.name = 'description';
      inputDescription.value = description;
      edit_form.appendChild(inputDescription);
      edit_form.submit();
    }
  });
```