もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

久々にnuxt入門した #05

はじめに

こんばんは。

今回も前回の続きです。

kojirooooocks.hatenablog.com

kojirooooocks.hatenablog.com

kojirooooocks.hatenablog.com

kojirooooocks.hatenablog.com

今回は、ついにモーダル表示やりました。

本題

まず vue-js-modal をインストールします。

www.npmjs.com

plugins/vue-js-modal.js

vue-js-modalの設定ファイル作成します。

import Vue from 'vue'
import VModal from 'vue-js-modal'

Vue.use(VModal, {
  dynamic: true,
  dynamicDefaults: { clickToClose: true },
  injectModalsContainer: true
})

nuxt-config.js

pluginを読み込みます

...
plugins: [{ src: '~/plugins/vue-js-modal.js' }],
...

index.vue

新たに編集時のイベントを渡しています。

<template>
  <div class="bg-blue-100">
    <h1 class="text-2xl">Todo List</h1>

    <div class="flex">
      <CardList
        type="todo"
        :card-list="cardList.todo"
        @doAddCard="doAddCard"
        @doModifyCard="doModifyCard"
      />
      <CardList
        type="doing"
        :card-list="cardList.doing"
        @doAddCard="doAddCard"
        @doModifyCard="doModifyCard"
      />
      <CardList
        type="done"
        :card-list="cardList.done"
        @doAddCard="doAddCard"
        @doModifyCard="doModifyCard"
      />
    </div>
  </div>
</template>

<script>
import CardList from '../components/CardList'
export default {
  components: { CardList },
  data() {
    return {
      cardList: {
        todo: [
          { title: 'aaa', body: 'AAA', status: 'todo' },
          { title: 'bbb', body: 'BBB', status: 'todo' },
          { title: 'ccc', body: 'CCC', status: 'todo' },
          { title: 'ddd', body: 'DDD', status: 'todo' },
          { title: 'eee', body: 'EEE', status: 'todo' },
          { title: 'fff', body: 'FFF', status: 'todo' }
        ],
        doing: [{ title: '○○○○○', body: '◎◎◎◎◎◎◎', status: 'doing' }],
        done: [
          { title: '000', body: '0000000000', status: 'done' },
          { title: '111', body: '1111111111', status: 'done' },
          { title: '222', body: '2222222222', status: 'done' }
        ]
      }
    }
  },
  methods: {
    doAddCard(type, title) {
      this.cardList[type].push({
        title,
        body: ''
      })
    },
    doModifyCard(type, key, title, body) {
      this.cardList[type][key].title = title
      this.cardList[type][key].body = body
    }
  }
}
</script>

<style></style>

CardList.vue

実際にmodalの表示を行っています。

<template>
  <div class="flex-1 bg-gray-100 m-2 cursor-pointer">
    <p class="text-xl ml-2">{{ type }}</p>
    <Card
      v-for="(card, k) in cardList"
      :key="k"
      :card-key="k"
      :card-title="card.title"
      @showModal="showModal"
    />
    <modals-container @onModifyCard="onModifyCard" />
    <AddCard @onAddCard="onAddCard" />
  </div>
</template>

<script>
import Card from './Card'
import AddCard from './AddCard'
import CardDetailModal from './CardDetailModal'
export default {
  name: 'CardList',
  components: { AddCard, Card },
  props: {
    type: {
      type: String,
      require: true,
      default: ''
    },
    cardList: {
      type: Array,
      require: true,
      default: null
    }
  },
  methods: {
    showModal(cardKey) {
      this.$modal.show(
        CardDetailModal,
        {
          cardKey,
          cardData: this.cardList[cardKey]
        },
        {
          height: 500
        }
      )
    },
    onAddCard(title) {
      this.$emit('doAddCard', this.type, title)
    },
    onModifyCard(cardKey, cardTitle, cardBody, cardStatus) {
      this.$emit('doModifyCard', cardStatus, cardKey, cardTitle, cardBody)
      this.$modal.hideAll()
    }
  }
}
</script>

<style scoped></style>

Card.vue

カード一つ一つにイベントを設定します。

<template>
  <div class="z-10 flex-auto bg-white m-2 shadow">
    <p class="w-full p-3" @click="$emit('showModal', cardKey)">
      {{ cardTitle }}
    </p>
  </div>
</template>

<script>
export default {
  props: {
    cardKey: {
      type: Number,
      require: true,
      default: 0
    },
    cardTitle: {
      type: String,
      require: true,
      default: ''
    }
  }
}
</script>

<style scoped></style>

CardDetailModal.vue

modalの中身です。

<template>
  <div class="m-8">
    <h2 class="text-3xl">{{ cardData.title }}の修正</h2>
    <div class="mt-5">
      <div>
        <p class="my-1 text-lg">タイトル</p>
        <input
          v-model="title"
          class="w-full p-3 border-2"
          @focus="$event.target.select()"
        />
      </div>

      <div class=" mt-5">
        <p class="my-1 text-lg">本文</p>
        <textarea
          v-model="body"
          class="w-full p-3 border-2"
          rows="6"
          @focus="$event.target.select()"
        ></textarea>
      </div>

      <div class="mt-5 text-right">
        <button
          class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
          @click="$emit('onModifyCard', cardKey, title, body, cardData.status)"
        >
          追加
        </button>
        <button
          class="bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow"
          @click="$emit('close')"
        >
          閉じる
        </button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'CardDetailModal',
  props: {
    cardKey: {
      type: Number,
      require: true,
      default: 0
    },
    cardData: {
      type: Object,
      require: true,
      default: () => {}
    }
  },
  data() {
    return {
      body: this.cardData.body,
      title: this.cardData.title
    }
  }
}
</script>

<style scoped></style>

Image from Gyazo

終わりに

最初の方読み込みが悪いのか、emitを設定できなかったり、しかし再読みこすると設定できたりと思わぬトラブルがありましたが、なんとかなりました。

emitの使い方(というよりコンポーネントの分け方?)が多分下手で、無駄にいっぱい引数取り回しててちょっとダサいです。。。

まぁ、できたので良し!

次はバリデーションをやってみます。

ではまた。