マルチフィールドのコンテンツ出力方法(Vue.js 版)

この記事はMTAppjQuery Advent Calendar 2018の2日目の記事です。

前回はマルチフィールドのコンテンツ出力方法を紹介しました。
今日の記事では、前回設定したフィールドをMTMLではなくVue.jsで描画する方法になります。

v2.3.0 mt:Foreachmt:NestVar 実装される前に mt:Loop で行っていたのですが、 JSONが深いという問題があってかなりテンプレートがカオスになっていました。
開発者の奥脇さんにVue.jsで描画してみては?となったので、試しに実装してみたという経緯です。

Vue.jsが知りたい方は公式ページを参照してみてはいかがでしょうか。

MTAppjQuery 2からVue.jsが同梱されてマルチフィールドもVue.jsで書かれています。
Vue.jsでJSONを保存してVue.jsで出力してみたという紹介になります。

はじめに

  • ブロックフィールドは、前回の記事のものを使用しています
  • user.js Codeは、前回の記事のものを使用しています

Tips:Vue.jsで描画した例

Vue.jsでも mt:Loop のようにテンプレだけで取り出すことが可能ですが、それでは mt:Loop で書いたのと同じような状態になってしまいます。
本体のJSONはそのままでVue.jsのテンプレで取り出しやすいようにJSONを加工して出力させました。

Vue.jsを出力するテンプレに読み込ませる

  
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
  

Vue.jsのcomputedで取得したJSONを加工する

取得したJSONをそのまま data にいれてテンプレートに使ってしまうと mt:Loop と同じように深い値は潜らないといけません。
それでは見通しという面ではあまり恩恵を受けれないと思います。取得したJSONを取りやすいよう(並列)に computed で加工したものを v-for すると良いかと思いました。

  • Vue.jsの本体を読み込みます(スタンドアロン版)
  • 変数 items にマルチフィールドの値を to_json で出力
  • Object.keysとforEachで取得したJSONを回す
  • switch構文で条件にマッチしたラベルごとにdataの値に取り出した値を格納

switch構文にした理由としては、if文では読みにくいのとブロックが増えたときに対応しやすくするためです。
forでも良かったのですが、ちょっとforEachを普通に使ってみたかったのでObject.keysで回してみました。

  
// MTテンプレートで出力する場合は、itemsにMTタグで出力させる
var items = <mt:Var name="json" to_json="1" />
var vm = new Vue({
  el: '#app',
  data: {
    items: items,
    field_data: [],
    field_array_table: [],
    field_array_list: []
  },
  computed: {
    multiFieldJSON: function(){
      var field_data = this.field_data;
      console.log(items.items);
      /**
       * @type {Object}
       */
      Object.keys(items.items).forEach(function(value, index, array){
        var self_items = items.items[index].data;
        var check_array = Array.isArray(self_items);
        if ( check_array ) {
          // console.log(items.items[index].type);
          if (items.items[index].type === 'table') {
            // console.log(items.items[index].data.length);
            switch (items.items[index].label) {
              case '表組み' :
                // 配列の初期化
                this.field_array_table = [];
                console.log(items.items[index].data)
                Object.keys(items.items[index].data).forEach(function(data_value, data_index, data_array){
                  this.field_array_table.push({
                    title: items.items[index].data[data_index][0].data,
                    text: items.items[index].data[data_index][1].data
                  });
                })
                field_data.push({ table: this.field_array_table });
                break;
              case 'リスト' :
                // 配列の初期化
                this.field_array_list = [];
                Object.keys(items.items[index].data).forEach(function(data_value, data_index, data_array){
                  this.field_array_list.push({
                    url: items.items[index].data[data_index][0].data,
                    text: items.items[index].data[data_index][1].data
                  });
                })
                field_data.push({ list: this.field_array_list });
                break;
              default :
                break;
            }
          } else if(items.items[index].type === 'multi-column-content') {
              switch (items.items[index].label) {
                case '画像単体アップロード + テキスト' :
                  field_data.push({
                    figure_url: items.items[index].data[1][0].url,
                    figure_text: items.items[index].data[0][0].data
                  });
                  break;
                default :
                  break;
              }
          }
        } else {
          // 単体入力値の場合
          switch (items.items[index].type) {
            /*
             * 配列にオブジェクトを追加する場合は、pushに対して、{ key名 : value名 }で追加する
             */
            case 'h1' :
              field_data.push({ h1: items.items[index].data });
              break;
            case 'h2' :
              field_data.push({ h2: items.items[index].data });
              break;
            case 'h3' :
              field_data.push({ h3: items.items[index].data });
              break;
            case 'text' :
              field_data.push({ text: items.items[index].data });
              break;
            case 'textarea' :
              field_data.push({ textarea: items.items[index].data });
              break;
            case 'image' :
              field_data.push({ image: items.items[index].url });
              break;
            default :
              break;
          }
        }
      });
      return field_data;
    }
  }
});
  

Vue.jsのテンプレート

加工したJSONを描画するテンプレートは以下のようになります。
複雑にテンプレート書くのではなく、取り出しやすいようにJS側で制御させてからテンプレートに落とし込むようにします。

  • multiFieldJSONでループ
  • v-ifを使ってマッチした値で描画
  • {{ XXX }} Mustache記法で描画
  
  <template v-for="(key, value) in multiFieldJSON">
    <template v-if="key.h1"><h1 class="uk-heading-bullet">{{ key.h1 }}</h1></template>
    <template v-if="key.h2"><h2 class="uk-heading-line"><span>H{{ key.h2 }}</span></h2></template>
    <template v-if="key.h3"><h3 class="uk-heading-divider">{{ key.h3 }}</h3></template>
    <template v-if="key.text"><p class="uk-text-small">{{ key.text }}</p></template>
    <template v-if="key.textarea">
      <p class="uk-text-small" v-html="key.textarea.replace(/\n/g,'<br/>')"></p>
    </template>
    <template v-if="key.image">
    <div class="uk-margin-auto uk-margin-auto-vertical uk-width-1-2@s uk-card uk-card-default uk-card-body">
      <img v-bind:src="key.image">
    </div>
    </template>
    <template v-if="key.table">
      <table class="uk-table uk-table-hover uk-table-divider">
        <tbody>
          <template v-for="(key, value) in key.table">
          <tr>
            <th>{{ key.title }}</th>
            <td>{{ key.text }}</td>
          </tr>
          </template>
        </tbody>
      </table>
    </template>
    <template v-if="key.list">
      <ul class="uk-list uk-list-bullet">
        <li v-for="(key, value) in key.list">
          <a v-bind:href="key.url" v-if="key.url">{{ key.text }}</a>
          <template v-else>{{ key.text }}</template>
        </li>
      </ul>
    </template>
    <template v-if="key.figure_url">
      <div class="uk-flex-middle" uk-grid>
        <div class="uk-width-2-3@m">
          <p>{{ key.figure_text }}</p>
        </div>
        <div class="uk-width-1-3@m uk-flex-first">
          <img v-bind:src="key.figure_url">
        </div>
      </div>
    </template>
  </template>
  

前回の mt:Foreachmt:NestVar に比べるとJSの記述する量が増えますがテンプレート自体はみやすくなったと思います。
どちらを選ぶかはプロジェクトの要件だったりで変わりますが、Vue.jsでやる場合は上記の方法の参考になればと思います。

最終コードはこちら

最終的に出来たコードをGistに書いておきましたので、取り出しの参考になればと思います。

マルチフィールドで取り出しや機能要望などはサポートにお問い合わせしてみるのもいいと思います。
簡単な取り出し方の紹介でしたが、MTAppjQuery 2で実装されたマルチフィールドを活用してみてはいかがでしょうか。

Author

札幌でフロントエンドエンジニアとして働いています。好きな音楽はSIAM SHADE!
SIAM SHADEの六人目のメンバーとして日々ロックを愛し続けている。
HTML,CSS,JavaScriptをベースにCMS構築が得意です。
HAMWORKS社員 https://ham.works