ニフクラmBaaSお役立ちブログ

スマホアプリ開発にニフクラmBaaS。アプリ開発に役立つ情報をおとどけ!

マスタメンテナンス画面を作る(その2)「データ編集機能を作る」

前回の認証に続いて、今回はデータ編集機能を作っていきます。担当者のグループ(Manager)に読み書き権限を付与し、全体に対しては読み込み権限だけ付与します。そうすることで、ユーザからは誤ってデータ操作されないマスタができあがります。

今回はデータ表示、編集機能にVue.jsを使いました。Vue.jsはReactのようにデータと双方向性のある更新、表示ができるライブラリです。Reactよりも小さく、コンパクトな用途に向いています。

データを取得する

まずデータの取得部分を作ります。クラス名はHTML側で指定することとします。以下のHTMLのdata-classNameがそうです。

<a href="#" class="dataManage" data-className="Genre">データメンテナンス</a>

この .dataManage をクリックすると、データを表示します。 vm というのは Vue.jsのオブジェクトで、すでにレンダリング済みであれば表示はそのまま維持します。

var vm;
$(".dataManage").on('click', function(e) {
  e.preventDefault();
  $('.page').addClass('hide');
  $('.dataManagement').removeClass('hide');
  if (typeof vm == 'object') {
    return;
  }
  var className = $(e.target).data('classname')
  var ClassData = ncmb.DataStore(className);
  ClassData.fetchAll()
    .then(function(results) {
      // データ表示処理
    });
});

データを整形する

次にデータの表示を作りますが、まずデータが何もなかった場合を考えます。この場合、resultsに空のオブジェクトが返ってきます。

if (Object.keys(results).length == 0) {
  // 空の場合
  var data = {
    header: ['objectId'],
    body: []
  }
} else {
  // 空じゃない場合
  var data = {
    header: [],
    body: results
  };
  for (var i in results) {
    data.header = data.header.concat(Object.keys(results[i]));
  }
  data.header = data.header.filter(function(value, index, self) {
    return self.indexOf(value) == index;
  });
}

resultsはデータが配列で返ってきますが、その構造は行によって異なります。データが入っていない場合はキーもありません。そのため、まず取得した全データを使ってヘッダーになるキーを生成します。その後、フィルタリングしてユニークな文字列だけにします。

データを表示する

加工したヘッダーを使ってVue.jsに適用します。 #maintenance に対して適用します。

var id = 1;
vm = new Vue({
  el: '#maintenance',
  data: {
    data: data,
    className: className
  },
  methods: {
    // 各種メソッド
  }

methods の中にデータを追加したり、カラムを追加すると言った表示を更新する処理を記述していきます。

実際の表示は index.html 内に記述します。全体像としては次のようになります。データを表示する際には {{ と }} で囲みます。onClickイベントは v-on:click で、繰り返し処理は v-for になります。さらに処理分岐を行う場合には v-if という定義を用います。 v-if は v-else も使えます。

<div class="dataManagement hide page">
  <div id="maintenance">
    <h2>{{ className }}</h2>
    <div class="actions">
      <a v-on:click="add_row">行追加</a>
    </div>
    <table class="table table-striped maintainance-table">
      <thead>
        <tr>
          <td></td>
          <td v-for="column in data.header">
            <span v-if="['acl', 'createDate', 'updateDate'].indexOf(column) < 0">{{column}}</span>
          </td>
          <td>
            <a v-on:click="add_cloumn">カラム追加</a>
          </td>
        </tr>
      </thead>
      <tbody v-if="data.body.length == 0">
        <tr><td>まだデータがありません</td></tr>
      </tbody>
      <tbody v-else>
        <tr v-for="row in data.body" :key="row.objectId">
          <td>
            <a v-on:click="delete_row(row)">削除</a>
          </td>
          <td v-for="column in data.header">
            <span v-if="['acl', 'createDate', 'updateDate'].indexOf(column) < 0">
              <span v-if="column == 'objectId'">{{ row.objectId || '新規'}}</span>
              <input v-else type="text" class="form-control" @blur="done_edit(row)" v-model="row[column]" />
            </span>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

入力するテキストボックスで、以下のように定義します。 @blurはフォーカスを外れた時に呼び出すメソッドで、 v-modelは更新対象のデータになります。引数に Vue.js の繰り返しで使っている row がそのまま使えます。

<input v-else type="text" class="form-control" @blur="done_edit(row)" v-model="row[column]" />

カラムを追加する

データが何もない状態では、まずカラムの追加を行います。今回は以下のように定義されているので、Vue.jsのmethodsの中にadd_rowとして定義します。

<a v-on:click="add_cloumn">カラム追加</a>

この処理は次のようになります。カラム名を入力してもらい、それを this.data.header に対して追加します。このように Vue.js のthis.data を更新すれば表示も自動的に更新されます。

add_cloumn: function() {
  var columnName = prompt("カラム名を入力してください");
  this.data.header.push(columnName);
},

データを追加する

次にデータを追加します。HTML側で、次のように指定します。

<a v-on:click="add_row">行追加</a>

この時の処理は次のようになります。_id は便宜上つけているもので、新規データが複数あった場合の見分けをつけるためのものです。注意点として、ncmb.Aclをあらかじめ定義してデータに紐付けている点があります。これによりデータの保存を行った際に担当者のみ書き込みができます(読み込みはPublicにも有効としています)。できあがったデータを this.data.body に追加すれば完了です。

add_row: function() {
  var item = new ClassData({_id: id});
  var acl = new ncmb.Acl;
  acl.setRoleReadAccess(roles['Manager'], true);
  acl.setRoleWriteAccess(roles['Manager'], true);
  acl.setPublicReadAccess(true);
  item.set('acl', acl);
  this.data.body.push(item);
  id++;
},

これでカラムを追加したり、データの入力ができるようになります。

データを保存する

mBaaSへの保存処理は入力のフォーカスが外れたタイミングになります。 @blur=done_edit(row) としていますので、done_editが呼ばれます。この時、行のデータ(データストアのインスタンス)も入ってきます。新規データの場合、 _id は余計なので削除します。また、新規保存の場合は save、更新の場合は update を呼び出します。

mBaaSへの保存処理が終わったら、 vm.$forceUpdate(); を呼び出して表示を強制的に更新します。mBaaS側でrowを更新してもVue.jsに伝搬しなかったので、このようにしています。

done_edit: function(row) {
  var me = this;
  if (typeof row.objectId == 'undefined') {
    // mBaaSへ未保存の場合
    delete row['_id'];
    row.save()
      .then(function(obj) {
        showMessage('success', '作成しました');
        vm.$forceUpdate();
      });
  } else {
    // mBaaSに保存済みの場合
    row.update()
      .then(function(obj) {
        showMessage('success', '更新しました');
        vm.$forceUpdate();
      })
  }
}

データを削除する

最後にデータの削除です。これは次のように定義しています。

<a v-on:click="delete_row(row)">削除</a>

そこで呼ばれるのは delete_row です。この場合、mBaaSに保存前と保存後の2パターンが考えられますので、次のような処理になります。

delete_row: function(row) {
  var me = this;
  if (typeof row.objectId == 'undefined') {
    // mBaaSへ未保存の場合
    this.data.body = this.data.body.filter(function(data) {
      return data._id != row._id;
    });
    showMessage('success', '削除しました');
  } else {
    // mBaaSに保存済みの場合
    row.delete()
      .then(function() {
        me.data.body = me.data.body.filter(function(data) {
          return data.objectId != row.objectId;
        });
        showMessage('success', '削除しました');
      })
  }
},

これでデータのメンテナンス(データの追加、更新、削除)に対応しました。


Vue.js を使うことで柔軟なデータ表示を行うmBaaSでも分かりやすい管理画面が作成できます。

今回までのコードは NCMBMania/DataMaintenance にアップしてあります。参考にしてください。