質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.58%

Vue.js Vuexを使ったタスク管理カレンダーでVuexへのタスク追加をカレンダーに反映させたい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 699

score 2

前提・実現したいこと

Vue.js Vuexを使ったカレンダーにタスクを追加出来るアプリを開発しているのですが、Vuexに追加したタスクを即座にカレンダーに反映したいです

発生している問題・エラーメッセージ

日付ごとに<td></td>をリストレンダリングしてカレンダーを作成して行きつつ、methodsプロパティ内にあるgetMessageTextメソッドによって日付オブジェクトごとに与えられた年月日(例: 2020_08_22)の値と一致する日付を持つタスクがステート内に存在する場合、<td></td>内に、<span>タスク内容</span>の形で挿入して反映しています。

新たにストアのtasks配列にフォームを通じてデータをコミットした際、リストレンダリングを再描画させ、タスクをカレンダーに反映させたいのですが上手い方法が見つからず攻めあぐねています

getMessageText(calendarDate) {
      for (let i = 0; i < this.tasks.length; i++) {
        const message = this.tasks.filter(date => date.date === calendarDate)[0]
        return message ? message.name : ''
      }
}

該当のソースコード

<template>
  <div class="calendar-wrapper example-modal-window">
    <table>
      <thead>
        <tr>
          <th id="prev" @click="prevPage">&laquo;</th>
          <th id="title" colspan="5">{{getTitleDate}}</th>
          <th id="next" @click="nextPage">&raquo;</th>
        </tr>
        <tr>
          <th>Sun</th>
          <th>Mon</th>
          <th>Tue</th>
          <th>Wed</th>
          <th>Thu</th>
          <th>Fri</th>
          <th>Sat</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(week, index) in createCalendar" :key="index">
          <td
            v-for="day in week"
            :key="day.fullDate"
            :class="{disabled: day.isDisabled, today: day.isToday}"
            @click="openModal(day.fullDate)"
          >
          {{day.date}}
          <br><span>{{getMessageText(day.fullDate)}}</span>
          </td>
        </tr>
      </tbody>
    </table>
    <my-modal @close="closeModal" v-if="modal">
      <p>タスクを入力してください</p>
      <div><input type="text" v-model="message"></div>
      <template slot="footer">
        <button @click="doSend">送信</button>
      </template>
    </my-modal>
  </div>
</template>

<script>
import MyModal from './MyModal.vue'

export default {
  components: {
    MyModal
  },
  data() {
    return {
      month: "",
      year: "",
      today: "",
      modal: false,
      message: '',
      fullDate: ''
    };
  },
  created() {
    this.today = new Date();

    this.year = this.today.getFullYear();
    // 0 ~ 11
    this.month = this.today.getMonth();
  },
  computed: {
    createCalendar() {
      const dates = [
        ...this.getCalendarHead,
        ...this.getCalendarBody,
        ...this.getCalendarTail,
      ];
      // 一週間ごとのオブジェクトが配列にまとめて入っている
      const weeks = [];
      const weeksCount = dates.length / 7;

      for (let i = 0; i < weeksCount; i++) {
        weeks.push(dates.splice(0, 7));
      }

      return weeks;
    },

    getTitleDate() {
      return `${this.year}/${String(this.month + 1).padStart(2, "0")}`;
    },

    getCalendarHead() {
      const dates = [];
      const d = new Date(this.year, this.month, 0).getDate();
      const n = new Date(this.year, this.month, 1).getDay();

      for (let i = 0; i < n; i++) {
        dates.unshift({
          date: d - i,
          isToday: false,
          isDisabled: true,
          fullDate: `${this.year}_${String(this.month).padStart(2, '0')}_${String(d - i).padStart(2, '0')}`,
        });
      }

      return dates;
    },

    getCalendarBody() {
      const dates = []; // date: 日付, day: 曜日
      const lastDate = new Date(this.year, this.month + 1, 0).getDate();

      for (let i = 1; i <= lastDate; i++) {
        dates.push({
          date: i,
          isToday: false,
          isDisabled: false,
          fullDate: `${this.year}_${String(this.month + 1).padStart(2, '0')}_${String(i).padStart(2, '0')}`,
        });
      }
      // 年月がthis.todayと一致するとき実行
      if (
        this.year === this.today.getFullYear() &&
        this.month === this.today.getMonth()
      ) {
        // dates配列は0から始まる
        dates[this.today.getDate() - 1].isToday = true;
      }

      return dates;
    },

    getCalendarTail() {
      const dates = [];
      const lastDay = new Date(this.year, this.month + 1, 0).getDay();

      for (let i = 1; i < 7 - lastDay; i++) {
        dates.push({
          date: i,
          isToday: false,
          isDisabled: true,
          fullDate: `${this.year}_${String(this.month + 2).padStart(2, '0')}_${String(i).padStart(2, '0')}`,
        });
      }

      return dates;
    },

    tasks() {
      return this.$store.state.tasks
    },
  },
  methods: {
    prevPage() {
      this.month--;
      if (this.month < 0) {
        this.year--;
        this.month = 11;
      }
    },
    nextPage() {
      this.month++;
      if (this.month > 11) {
        this.year++;
        this.month = 0;
      }
    },
    openModal(value) {
      this.modal = true
      this.fullDate = value
    },
    closeModal() {
      this.modal = false
    },
    doSend() {
      if(this.message.length > 0) {
        this.$store.commit('addTask', {message: this.message, date: this.fullDate})
        this.message = ''
        this.closeModal()
      } else {
        alert('タスクを入力してください')
      }
    },
    getMessageText(calendarDate) {
      for (let i = 0; i < this.tasks.length; i++) {
        const message = this.tasks.filter(date => date.date === calendarDate)[0]
        return message ? message.name : ''
      }
    }
  },
};
</script>

<style scoped>
.calendar-wrapper {
  height: 100vh;
}

table {
  border-collapse: collapse;
  border: 2px solid #eeeeee;
  width: 100%;
  height: 100%;
  table-layout: fixed;
}

thead {
  background: #eeeeee;
}

th,
td {
  padding: 8px;
  text-align: center;
  height: 50px;
  border: 1px solid #eeeeee;
}

td {
  vertical-align: top;
}

tbody td:first-child {
  color: red;
}

tbody td:last-child {
  color: blue;
}

td.disabled {
  opacity: 0.3;
}

td.today {
  font-weight: bold;
}

#prev,
#next,
#today {
  cursor: pointer;
  user-select: none;
}
</style>
import Vue from 'vue'
import Vuex from 'vuex'
import App from './App.vue'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    tasks: [
      {
        id: 1,
        name: 'Vue.jsの勉強をする',
        date: '2020_08_22'
      },
      {
        id: 2,
        name: 'ご飯を食べる',
        date: '2020_09_03'
      }
    ],

    nextTaskId: 3
  },
  mutations: {
    addTask(state, payload) {
      state.tasks.push({
        id: state.nextTaskId,
        name: payload.message,
        fullDate: payload.date
      })
      console.log(state.tasks)

      state.nextTaskId++
    }
  }
})

Vue.config.productionTip = false

new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

mutationする際の日付情報のプロパティ名とリストレンダリングする際のプロパティ名が一致していないのが原因でした

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.58%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る