0

Good afternoon, I’m trying to animate the swap animation in my calendar using the transition-group and pops up such an error, I understand that it says “do not use v-for cycle indexes as a key”, but when I remove them, it leads to another error that everything items in the transition-group must have their own unique key. Also, maybe you know some useful tutors or any articles that may be useful for me to understand the transition, please give the link.

Screenshot of Calendar and Error

    <template>
  <div class="all">
      <div class="pagination">
        <div @click="prevPage" class="btn-left"><</div> 
        <p>{{ nameOfOneMonth }} {{ year }}</p>
        <div @click="nextPage" class="btn-right">></div> 
      </div>

        <div class="d_nameOfDays">
          <li v-for="day in nameOfDays" class="nameOfDays">{{ day }}</li>
        </div>
        <transition-group name="fade" >
          <div v-for="(week, i) in getCalendar" class="d_day" :key = "i">
            <li v-for="(day, h) in week" class="li_day" :key = "h">
            <div class="day" 
               v-bind:class="{ 'grey': isAnotherMonth(i, day), 'currentDay': currentDayOnCalendar(day) }"
               >{{ day }}</div>
          </li>
        </div>
        </transition-group>
  </div> 
</template>

<script>

export default {
  data(){
    return{
      currentPage: 0,
      namesOfMonths: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
      nameOfOneMonth: '',
      nameOfDays: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'],
      date: new Date(),
      isActive: true,
      year: ''
    }
  },
  computed: {
    getCalendar(){
      return this.buildCalendar();
    }
  },
  mounted(){
    this.year = this.date.getFullYear();
    this.currentPage = this.date.getMonth();
    this.nameOfOneMonth = this.namesOfMonths[this.currentPage];
  },
  methods: {
    prevPage(){
      if (this.currentPage === 0) {
        this.currentPage = 12;
        this.year--;
      }
      this.currentPage--;
      this.nameOfOneMonth = this.namesOfMonths[this.currentPage];
    },
    nextPage(){
      if (this.currentPage === 11) {
        this.currentPage = -1;
        this.year++;
      }
      this.currentPage++;
      this.nameOfOneMonth = this.namesOfMonths[this.currentPage];
    },
    isAnotherMonth(weekIndex, dayNumber) {
      if(weekIndex === 0 && dayNumber > 15) {
        // первая неделе и номер дня > 15
        return true
      }
      if (weekIndex === 4 && dayNumber < 15) {
        // последняя неделя и номер дня < 15
        return true
      }
      // день принадлежит текущему месяцу
      return false
    },
    currentDayOnCalendar(dayNumber){
      if(this.currentPage === this.date.getMonth() && dayNumber === this.date.getDate()){
        return true
      }
      return false
    },
    getYear(){
      this.year = this.date.getFullYear();
    },
    getLastDayOfMonth(month) { // нахождение числа последнего дня в месяце
      let dateDaysInMonth = new Date(this.year, month + 1, 0);
      return dateDaysInMonth.getDate();
    },
    getNumberOfFirstDayInMonth(month){ //нахождение номера первого дня в месяце
      let dateFirstDayInMonth = new Date(this.year, month, 1);
      return dateFirstDayInMonth.getDay();
    },
    buildCalendar(){
      let massOfMonth = [];
      for (let months = 0; months < 12; months++){
        massOfMonth.push(months);
        massOfMonth[months] = [];
        for ( let daysInMonth = 1; daysInMonth <= this.getLastDayOfMonth(months); daysInMonth++){
          massOfMonth[months].push(daysInMonth);
        }
        // Заполняем начало каждого месяца числами из прошлого месяца
        if(this.getNumberOfFirstDayInMonth(months) > 0){
          let t = this.getLastDayOfMonth(months-1) + 1;
          for(let b = 0; b <= this.getNumberOfFirstDayInMonth(months) - 2; b++){
            t--;
            massOfMonth[months].unshift(t)
          }
        }else if(this.getNumberOfFirstDayInMonth(months) === 0){
          let t = this.getLastDayOfMonth(months-1) + 1;
          for(let nulldays = 0; nulldays <= 5; nulldays++){
            t--;
            massOfMonth[months].unshift(t);
          }
        }
        //Заполняем конец каждого месяца числами из будущего месяца
        if(this.getNumberOfFirstDayInMonth(months + 1) > 1){
          let t = 0;
          for(let q = this.getNumberOfFirstDayInMonth(months + 1); q <= 7; q++){
            t++;
            massOfMonth[months].push(t);
          }
        } else if(this.getNumberOfFirstDayInMonth(months + 1) === 0){
          massOfMonth[months].push(1);
        }
      }

      // разбиение большого массива месяц на 
      // меньшие массивы которые имеют по 7 элементов
      var longArray = massOfMonth[this.currentPage];
      var size = 7;

      var newArray = new Array(Math.ceil(longArray.length / size)).fill("")
          .map(function() { 
            return this.splice(0, size) 
          }, longArray.slice());
       //--------------------------------------------------   
        return newArray; // вывод самого календаря
    }
  }
};
</script>

<style>
  body{
    background-color: #FAFAFA;
  }
  .pagination{
    display: grid;
    height: 40px;
    grid-template-columns: 1fr 4fr 1fr;
    margin: 20px 80% auto 5%;
    background-color: white;
  }
  .btn-left, .btn-right{
    padding: 10px 10px;
    height: 20px;
    font-weight: bold;
    font-size: 18px;
  }
  .btn-left:hover, .btn-right:hover{
    background-color: #9D9D9D;
    color: white;
    cursor: pointer;
  }
  .pagination p{
    text-align: center;
    font-size: 18px;
    margin-top: 10px;
    font-weight: bold;
  }
  .d_nameOfDays{
    display: grid;
    height: 25px;
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
    margin: 0 80% auto 5%;
    background-color: #DEDEDE;;
    list-style-type: none;
    text-align: center;
    padding-top: 5px;
  }
  .d_day{
    display: grid;
    height: 23px;
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
    margin: 0 80% auto 5%;
    background-color: white;
    list-style-type: none;
    text-align: center;
    padding-top: 3px;
  }
  .day{
    border: 1px solid white;
  }
  .day:hover{
    cursor: pointer;
    border-radius: 10%;
    border: 1px solid #BAAAAA;
  }
  .grey{
    color: #BAAAAA;
  }
  .currentDay{
    background: #16B9DE; 
    border-radius: 10%;
  }

2 Answers2

0

Key of component is important because it allows Vue to track which element exactly that is. When you use index, the same index might be assigned to a different component. Array can be resorted or element can be removed and while you know it won't (weekdays won't change), Vue has no such certainty. This is what this error message is trying to tell you.

In order to address it, you could add explicit index to each element. For instance weekdays could have the following format:

const weekdays = [
    { id: 1, name: 'Monday' },
    { id: 2, name: 'Tuesday' },
    // ...
    { id: 7, name: 'Sunday' },
];

Then you could use id property as a unique value for your key.

The Witness
  • 910
  • 7
  • 12
  • So I have a cycle that goes through the names of the days of the week, not even in the transition. I will not also independently set the id for 31 days and this is only in one month, and there are 12 of them. – Andrey Belichenko Jul 19 '19 at 12:36
  • If you don't want to see this error then you cannot use index for a key. My proposition was only a proposition, maybe even merely an illustration. If you can find some smarter way, go for it. – The Witness Jul 19 '19 at 12:38
  • Sorry, I also do not know, I myself understand everything that you wrote above, I have already tried to use Date.now () as the id, but it did not help – Andrey Belichenko Jul 19 '19 at 12:42
  • `Date.now()` if called every time, will be returning different values. Later, I'll check your code more thoroughly and maybe something pops in my head. – The Witness Jul 19 '19 at 12:54
  • I will be very grateful to you – Andrey Belichenko Jul 19 '19 at 12:59
  • Can you add in your post what `this.getCalendar` is returning? It's an array but it's difficult to read through the code. – The Witness Jul 19 '19 at 13:01
  • To be honest, I'm beginer and did not understand what you want from me, let me give you my GitHub https://github.com/belichenk0andreyka/Calendar/blob/master/src/App.vue – Andrey Belichenko Jul 19 '19 at 13:13
0

I found a solution to this problem, the following actions helped me. Remove key from .d_day and .li_day elements. The .d_day elements added a shared div, set the currentPage as the key to the current div, and replaced the transition-group with the transition.

<template>
  <div class="all">
      <div class="pagination">
        <div @click="prevPage" class="btn-left"><</div> 
        <p>{{ nameOfOneMonth }} {{ year }}</p>
        <div @click="nextPage" class="btn-right">></div> 
      </div>

        <div class="d_nameOfDays">
          <li v-for="day in nameOfDays" class="nameOfDays">{{ day }}</li>
        </div>
        <transition name="fade" >
          <div :key="currentPage">
            <div v-for="(week, i) in getCalendar" class="d_day">
            <li v-for="day in week" class="li_day">
            <div class="day" 
               v-bind:class="{ 'grey': isAnotherMonth(i, day), 'currentDay': currentDayOnCalendar(day) }"
               >{{ day }}</div>
              </li>
            </div>
          </div>
        </transition>
  </div> 
</template>