Vue.jsで実装しているブロックエディタの汎用化に向けた考察

公開
更新

2月5日に「Vue.jsでPowerCMS Xのカスタム編集タイプを試作」で紹介したとおり、Vue.jsでPowerCMS Xのカスタム編集タイプの実装を試しています。現在、設計済のHTML/CSSコンポーネントに対応した入力フィールドを用意するブロックエディタの実装ができています。
コンポーネントベースのブロックエディタ試作品を利用している画面

このエディタを汎用的に利用できるよう整えようとした時、目下2点気になるところがあります。

  • Vue.jsのテンプレート構文を用いて入力フィールドのUIを記述する必要があること
  • ブロックの編集データを保存するためのデータ構造をオブジェクト(≒JSON)で定義する必要があること

UIの記述について

例えば見出しブロック…見出しテキストと見出しレベルを入力する以下のようなブロックで観察します。
見出しテキストと見出しレベルを入力するフィールドを備えたブロックの画面

テンプレートは以下の通りです。PowerCMS Xが採用しているBootstrap v4.0を使用しています。

<input type="text" v-model="element.text" class="form-control">
<div class="mt-2">
  <label class="custom-control custom-radio">
    <input class="custom-control-input" type="radio" v-model="element.heading_level" value="2">
    <span class="custom-control-indicator"></span>
    <span class="custom-control-description">見出し2</span>
  </label>
  <label class="custom-control custom-radio">
    <input class="custom-control-input" type="radio" v-model="element.heading_level" value="3">
    <span class="custom-control-indicator"></span>
    <span class="custom-control-description">見出し3</span>
  </label>
  <label class="custom-control custom-radio">
    <input class="custom-control-input" type="radio" v-model="element.heading_level" value="4">
    <span class="custom-control-indicator"></span>
    <span class="custom-control-description">見出し4</span>
  </label>
</div>

通常name="text"と書くところをv-model="element.text"と書くところが大きな違いです。記述リストのようにdtddを増減するフィールドや2カラム構成だとVue.js独特の記述が先のコードよりも多く出てきますが、増減の仕組みがなければ非常にシンプルな記述であることが分かります。

画像を指定するフィールドはどうでしょうか。
見出しテキストと見出しレベルを入力するフィールドを備えたブロックの画面

画像を指定するフィールドのUIを記述するのは複雑なのですが、Vueコンポーネント化してあります。よって、以下のような短い記述で済みます。

<assetselector :element="element" />

データ構造について

Vue.jsはデータを操作すればリアクティブにUIが再描画される仕組みなので、ブロックを追加する時はデータプロパティにオブジェクトを追加しています。先の見出しブロックであれば、以下のようなオブジェクトを追加します。

{
    order: 1,
    text: "",
    heading_level: 2,
}

textheading_levelはUIを記述する時に指定したv-modelと紐付いています。このオブジェクトをどのようにして作り出すかが課題ですが、以下のような方法を考えています。

  • UIのテンプレートを解析してv-modelの値を集める
  • 頻繁に使われるキー(textなど)はチェックボックスで指定、独自なキーは手動で入力してもらうようなUIをカスタムフィールドで用意する

独自なキーを手動入力するにしてもname属性値を集める感覚でできると思いますので、それほどハードルは高くないのではと考えています。

まとめ

まだ個人研究の段階で製品化(プラグイン化)等は決まっていませんが、シンプルなブロックであればブロックのデータを保存するモデルを用意してUIやデータ構造を入力してもらうことはそれほど難しくないと考えました。増減するフィールド・複数カラムの場合は難易度が上がりますが、定型の構文になる可能性が高いと見ています。

余談:ES modulesの利用

まもなくInternet Explorer 11のサポートが終了するため、ES modulesを使用することにしました。エディタアプリケーションのインスタンス生成とマウントは以下のようなコードで実現しています。複数ブロックエディタの配置も視野に入れたコードを検討中です。

<script type="module">
  import ComponentBlocks from './<mt:var name="component_blocks_asset_dir" escape />/js/component_blocks.js';
  import { BlockController, AssetSelector } from './<mt:var name="component_blocks_asset_dir" escape />/js/component_blocks_components.js';

  const app = Vue.createApp(ComponentBlocks);
  app.component('editor', Editor);
  app.component('draggable', vuedraggable);
  app.component('blockcontroller', BlockController);
  app.component('assetselector', AssetSelector);
  const vm = app.mount('#component_blocks_<mt:var name="__col_name__">');
  const savedData = document.getElementById('_saved_<mt:var name="__col_name__">').value;
  if (savedData) {
    vm.blocks = JSON.parse(savedData);
  } else {
    vm.blocks = [];
  }
</script>