Holmes開発者ブログ

契約マネジメントシステム「ホームズクラウド」の開発者ブログです

Nuxt.js上でFroala Editorを動作させる方法

Holmesでエンジニアをしている三澤です。

弊社のサービスであるホームズクラウドには、契約書のテンプレートを自由に作成することができる機能があります。この機能を実現する上で、軽量なJavaScript リッチテキストエディターのFroala Editor(公式サイト)を利用しています。Froala Editorは、vue.js版も提供されており、vue.jsで動作するサンプルはあるのですが、Nuxt.js上で動作するサンプルが見当たらなかったので忘備録として残したいと思います。

本投稿では、まず①Froala Editorを表示させます。その後、②独自カスタマイズボタンを追加させたいと思います。 ポイントは、FroalaEditorをプラグイン化して呼び出すこと、独自カスタマイズボタンを追加する場合、DOM操作が行われる処理を実行するタイミングに注意することです。

環境

  • macOS Catalina
  • node v12.16.1
  • npm 6.13.4

前提

create-nuxt-appでNuxt.jsの環境構築済み(SSRモード(※サーバーサイドレンダリングモード)、すべてデフォルト設定)

インストール

Froala Editor

npm install vue-froala-wysiwyg

※本記事作成時にインストールされたバージョンは3.1.0でした。

実装

まずFroala Editorを表示させることを実現したいと思います。

Froala Editorをプラグイン

続いてpluginsディレクトリ内に下記のファイルを作成します。 このファイルと、次項のnuxt.config.jsの編集によってどのコンポーネントからもFroala Editorを呼び出すことができるようになります。

  • ファイル名:froala.js

  • 場所:plugins/froala.js

//froala本体インポート
import 'froala-editor/js/plugins.pkgd.min.js';
//サードパーティプラグインインポート
import 'froala-editor/js/third_party/embedly.min';
import 'froala-editor/js/third_party/font_awesome.min';
import 'froala-editor/js/third_party/spell_checker.min';
import 'froala-editor/js/third_party/image_tui.min';
//cssインポート
import 'froala-editor/css/froala_editor.pkgd.min.css';
 
import Vue from 'vue'
import VueFroala from 'vue-froala-wysiwyg'
import 'froala-editor/js/froala_editor.pkgd.min.js'
Vue.use(VueFroala)
Vue.config.productionTip = false

nuxt.config.jsの編集

前項でプラグイン化したFroala Editorを呼び出せるようにします。 併せてjqueryを使用できるようにします。

//省略
/*
** Plugins to load before mounting the App
*/
plugins: [
  { src: '~/plugins/froala.js', ssr: false } //追加
],
//省略

これで準備は完了です。 これからFroala Editorを呼び出すコンポーネントを作成していきます。

Froala Editorを呼び出すコンポーネントを作成

Froala Editorはコンポーネント化しなくても、ページ内に直接タグを記述してもいいのですが、エディターのヘッダー定義や独自カスタマイズボタンをする場合、複数の箇所で記述するのはよろしくないのでコンポーネント化し、それをページから呼び出したいと思います。

ここで行っている処理の概要は下記の通りです。

  1. templateタグ内で、froalaと内部変数をバインディング

  2. propsで初期表示する文字列を親コンポーネントから受け取る

  3. data定義でFroala Editorのヘッダーボタンの定義やプレイスホルダー、イベントハンドラの定義

  4. created処理でFroala Editorで入力した文字列を内部で保持するモデルにpropsで受け取った初期表示の文字列を設定

<template>
  <div>
      <froala id="edit" :tag="'textarea'" :config="config" v-model="content"></froala>
  </div>
</template>

<script>

export default {
  props: {
    initContent: String
  },
  data () {
    return {
      config: {
        toolbarButtons: {
          'moreText': {
            'buttons': 
                 [
                     'bold', 'italic', 'fontSize','underline', 
                     'strikeThrough', 'textColor', 'backgroundColor',
                     'inlineClass','inlineStyle','clearFormatting'
                 ],
            'buttonsVisible': 3
          },
          'moreParagraph': {
            'buttons': 
                 [
                     'alignLeft', 'alignCenter','alignRight', 
                     'alignJustify', 'formatOLSimple', 'formatOL', 
                     'formatUL', 'lineHeight', 'outdent', 
                     'indent', 'quote'
                 ],
            'buttonsVisible': 3
          },
          'moreRich': {
            'buttons': ['insertImage', 'insertTable',
                              'specialCharacters', 'insertHR'],
            'buttonsVisible': 3
          },
          'moreMisc': {
            'buttons': ['undo', 'redo','insert'],
            'buttonsVisible': 3
          }
        },
        placeholderText: '入力してください',
        events: {
          initialized: function () {
            console.log('initialized')
          },
          input: function () {
            console.log('inputed')
          }
        }
      },
      content: null
    }
  },
  created : function () {
      this.content = this.initContent
  },
}
</script>

上記で作成したコンポーネントを呼び出すページを作成

先ほど作成したFroalaView.vueを呼び出すページを作成します。

  • ファイル名:index.vue

  • 場所:pages/index.vue

ここで行っている処理の概要は下記の通りです。

  1. templateタグ内で、FroalaViewタグにpropsとして初期表示文字列を渡す

  2. import構文で先ほど作成したコンポーネントを読み込む

  3. componentsプロパティでFroalaViewコンポーネント使用を宣言

  4. dataでエディターに渡す初期表示文字列を定義

<template>
  <div class="container">
    <div class="editor">
      <FroalaView :initContent="editorContent"></FroalaView>
    </div>
  </div>
</template>

<script>
import Vue from 'vue'
import FroalaView from '~/components/FroalaView'

export default {
  components: {
    FroalaView
  },
  data: () => ({
      editorContent: 
            'こんにちは、契約マネジメントシステム「ホームズクラウド」の開発者ブログです。'
  })
}
</script>

<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
.editor {
  width: 800px;
}
</style>

動作確認(Froala Editorが表示されること)

アプリケーションを実行し、localhost:3000にアクセスして確認します。

f:id:a-misawa:20200927205405p:plain

Froala Editorが表示されました。続いて、エディターのヘッダーに独自カスタマイズボタンを追加したいと思います。今回はFroala Editorのヘッダーにマークボタンを追加し、クリックすると、spanタグを挿入する、ということを実現したいと思います。

独自カスタマイズボタンを追加

カスタマイズボタンを追加する場合、Froala Editorを呼び出したコンポーネントに処置を追加します。

// 省略 created()の直下に、下記コードを追加
mounted : function () {
  const FroalaEditor = require ('froala-editor')
  FroalaEditor.DefineIcon('insert', {NAME: 'plus', SVG_KEY: 'add'});
  FroalaEditor.RegisterCommand('insert', {
    title: '要素を追加',
    focus: true,
    undo: true,
    refreshAfterCallback: true,
    callback: function () {
      this.html.insert('<span class="contractor-seal">挿入された要素</span>');
    }
  });
}
// 省略 最下行に下記コードを追加
<style>
  .contractor-seal {
    color : red;
    width: 65px;
    height: 65px;
    border: 1px solid red;
    border-radius: 4px;
    text-align: center;
  }
</style>

※注意点として、Nuxt.jsプロジェクト作成時にSSRモードで作成した場合、サーバー内でデータを取得してレンダリングしてくれます。しかしFroala Editorに独自のカスタムボタンを追加する場合、Frola Editor内でDOM操作が行われており、サーバーサイドで実行されると内部エラーが発生します。それを回避するためにFroala Editorの処理を実行するのはmounted(DOM作成後=クライアントサイド)にて行っています。

動作確認(独自カスタマイズボタンが追加され、ボタンが機能すること)

カーソル位置にタグを挿入されました

独自カスタマイズボタンが追加され、クリックすると正しくカーソル位置にタグが挿入されることが確認できました。

所感

このように簡単にNuxt.jsプロジェクトでFroala Editorを起動することができることがわかりました。注意点として、Vue.js(Nuxt.jsでも)でDOM操作を行うことは本来あまり勧められていませんがどうしても必要な時もあります(どうしても以前開発したjQueryで記述された処理を移行させたい、jQueryで動作するプラグインを動かしたいなど)。その場合、SSR環境の場合はDOM操作が行われるタイミングに注意する必要があります。

以上、Nuxt.js上でFroala Editorを動作させる忘備録でした。

最後に

Holmesはエンジニア・デザイナーを募集しています!
興味がある方はこちらからご連絡ください!

lab.holmescloud.com

lab.holmescloud.com