週休七日

趣味のこととか、技術のこととか、読書感想文とか

Vue.jsを再入門する 4

前回記事

luca3104.hatenablog.com

今回は編集機能と削除機能を実装します。

モーダルを発生させる

ボタンの設置

タスクの横にボタンを置き、押下時にモーダルが発生するようにします。
b-checkboxの後ろに

            <button
                class="button is-primary"
                v-on:click="editTask()">Edit</button>

を記述します。

モーダルの設置

モーダルを発生させるにはb-modalを使用します。

          <b-modal :active.sync="isModalActive" has-modal-card>
          </b-modal>

has-modal-cardを設定するとその中に設定したモーダルの中身がモバイルでも崩れずに見れるらしいです。とりあえず設定しておきます。

:active.syncでモーダルをON/OFFします。
トリガーとなるisModalActiveはdata内に定義します。

  data() {
    return {
      taskText: '',
      tasks: [],
      isModalActive: false
    }
  }

タスクの編集

editTask()の実装をします。 この関数が呼ばれたらisModalActivetrueにします。

    editTask() {
      this.isModalActive = true
    }

これでモーダルが発生するようになりました!
実際に確認します。
f:id:luca3104:20180306095631g:plain

モーダルの上にフォームを作る

今回は別Componentとして作っていきたいと思います。

ファイル作成

src/components 配下にEditModal.vue というファイルを作ります。

Buefyのモーダルサンプルを見よう見まねでフォームを作ります。

    <form action="">
        <div class="modal-card" style="width: auto">
            <header class="modal-card-head">
                <p class="modal-card-title">Edit</p>
            </header>
            <section class="modal-card-body">
                <b-field label="Task">
                    <b-input
                        type="task"
                        :value="task"
                        placeholder="タスクを入力してください"
                        required>
                    </b-input>
                </b-field>
            </section>
            <footer class="modal-card-foot">
                <button class="button" type="button">削除</button>
                <button class="button is-primary">完了</button>
            </footer>
        </div>
    </form>

modal-cardの詳細仕様がどこに書いてあるのか不明なのでサンプルを見ながら解説していきます。

modal-card以下にはmodal-card-headmodal-card-bodymodal-card-footがあります。

Modal Card API

modal-card-head

下の画像の上段のLoginの部分がmodal-card-headです。

modal-card-body

次にフォームが並んでいる中段がmodal-card-bodyです。

modal-card-foot

ボタンが並んでいる下段がmodal-card-footです。

f:id:luca3104:20180306223612p:plain

中には通常通りのhtmlを書いていきます。
見た目はこれで完了です。

削除ボタンを押下時の呼び出す関数をdeleteTask()、完了ボタンを押下時に呼び出す関数をeditedTask()とします。

コンポーネントを読み込む

コンポーネント(ここではToDo.vue)内で作成したコンポーネント(ここではEditModal)を読み込みます。
<script>内に

import EditModal from '@/components/EditModal'

と記述し、コンポーネントを読み込みます。
@はsrcまでのパスです。

そしてexport default {以下に

  components: {
    'edit-modal': EditModal
  },

を記述することで、コンポーネント内で<edit-modal>タグとして扱うことができます。

最後にb-modal以下にedit-modalを記述し、見た目の完成です。

          <b-modal :active.sync="isModalActive">
              <edit-modal></edit-modal>
          </b-modal> 

実際にこうなります。
f:id:luca3104:20180306225626g:plain

親子の連携

以上で見た目は完了しましたが、実際に値が移動していません。
コンポーネントから値の送信、子コンポーネントでの値の当て込み、子コンポーネントから親コンポーネントの関数実行まで一気にやります。

親から子へ

コンポーネントから子コンポーネントへ値を渡すために子コンポーネントにpropsを設定します。
今回は配列のindexとtaskが export default {以下にこちらを追記します。

  props: {
    'task': String
  },

そして親コンポーネントに送るための値を設定し、v-bindで子コンポーネントに値を渡します。
今回はformPropsという名前でモデルを用意しました。

          <b-modal :active.sync="isModalActive">
              <edit-modal v-bind="formProps" ></edit-modal>
          </b-modal>
      formProps: {
          task: ''
      }

次にeditTask()を修正し、editTask呼び出し時に編集対象のTaskの文字をeditingTextに代入するようにします。

    editTask(task, index) {
      this.formProps.task = task
      this.isModalActive = true
    },

モーダルを発生させるボタンは以下のようにします。

            <button
                class="button is-primary"
                v-on:click="editTask(task.message)">Edit</button>

これで親から子への送信は完了です。

コンポーネントでの値の当て込み

次に受け取った値をモーダル内のテキストフィールドに入るようにします。
data内にtaskTextを記述し、初期値としてpropsで受け取ったtaskを代入します。

  data() {
    return {
      taskText: this.task
    }
  },

そして前回と同様にb-inputv-model="taskText"を設定するとモーダル発生時にテキストフィールドにtaskTextの値が入って表示されます。

この時点で実行すると、以下のようになります。

f:id:luca3104:20180308095718g:plain

これで完了です

子から親の関数実行

次に子から親の関数を実行します。
これはカスタムイベントハンドラーを使用して実現します。

jp.vuejs.org

v-onを使用してイベントを購読します。

          <b-modal :active.sync="isModalActive">
              <edit-modal v-bind="formProps" v-on:edited="editedTask" v-on:delete="deleteTask"></edit-modal>
          </b-modal>

editedというイベントが発火した時にeditedTaskが実行され、deleteが発火した時にdeleteTaskを実行します。

editedTaskdeleteTaskは一旦モーダルを消す実装のみをします。

    editedTask() {
      this.isModalActive = false
    },
    deleteTask() {
      this.isModalActive = false
    }

次に子にて、ボタンを押下された際に親のイベントを発火するようにします。
まずmodal-foot内のボタンにv-on:clickを記述します。

            <footer class="modal-card-foot">
                <button class="button" type="button" v-on:click="deleteTaskChild()">削除</button>
                <button class="button is-primary" v-on:click="editedTaskChild()">完了</button>
            </footer>

次にdeleteTaskChildeditedTaskChildを実装します。

    editedTask() {
      this.$emit('edited')
    },
    deleteTask() {
      this.$emit('delete')
    }

$emitを使用することで親で購読しているイベントを発火することができます。

実行してみます。

f:id:luca3104:20180308104025g:plain

モーダルを消すことに成功しました。

削除を実装する。

次に削除の関数を実装します。
現在tasksという配列を持っていますが、編集時のindexが判断できないため、editingIndexdata内に宣言します。
次にリスト描画時のv-forindexを取得するように追記します。

          <div
              class="field task-field"
              v-for="(task, index) in tasks"
              v-bind:data="task"
              v-bind:key="task.text">

次にeditTask関数内でeditingIndexindexを代入するように追記します。

    editTask(task, index) {
      this.formProps.task = task
      this.editingIndex = index
      this.isModalActive = true
    },

次に編集ボタンが呼び出すeditTaskの引数にindexを追加します

            <button
                class="button is-primary"
                v-on:click="editTask(task.message, index)">Edit</button>

最後にdeleteTaskを実装して終わりです。
編集中のIndexの場所にあるtasks内の要素を削除し、保存します。

    deleteTask() {
      this.tasks.splice(this.editingIndex, 1)
      this.storeTasks()
      this.isModalActive = false
    }

これで削除機能が実装できました。

実行してみます。

f:id:luca3104:20180308105018g:plain

削除ができました!

編集を実装する

編集機能の実装をします。
編集中の文字列は子がtaskTextという変数で持っているのでこれを親に渡します。
$emit内で値を渡すには、this.$emit('イベント名', 値)という風にします。
今回は親にtaskTextを渡すのみなので以下のようになります。

    editedTaskChild() {
      this.$emit('edited', this.taskText)
    },

親のeditedTask()は引数をもつようにeditedTask(task)とします。
最後に、編集後のjson配列、置き換え、保存をしています。

    editedTask(task) {
      var editedData = { message : task, done: this.tasks[this.editingIndex].done}
      this.tasks[this.editingIndex] = editedData
      this.storeTasks()
      this.isModalActive = false
    },

これで全部の機能が揃いました。
実行してみます。

f:id:luca3104:20180308110416g:plain

再入門をして

Vue.jsの色々な機能を使用してみました。

まだまだ他にも色々な機能があり使いきれてはいないですが、今後も色々触っていきたいと思います。

今後、vuexも触っていきます。