Bonjour,

Voila je rencontre un petit problème avec mon code.

Ce que je fais

J'essaie de faire un petit datepicker à ma manière sans tuto histoire de m'entrainer à utiliser VueJS. J'ai bien vu que Jonathan avait fait une vidéo où il en fait un ;)

Donc pour être précis, mon datepicker est vraiment simple... On a dans un header le label du mois et de l'année ainsi qu'un bouton Mois suivant et Mois précédent. En dessous on a le calendrier sous forme de tableau (je ne sais d'ailleurs pas si un tableau est préconisé...) qui s'affiche.

J'ai un peu galérer pour que le système soit bien réactif (le temps de comprendre le fonctionnement du framework). Mais maintenant je peux enfin voyager de mois en mois avec un calendrier correct.

<template>
  <div class="ui-datepicker">
    <table class="table">
      <thead class="datepicker-header">
        <th>
          <span class="button previous" @click="previousMonth">
            <i class="fas fa-angle-left"></i>
          </span>
        </th>
        <th colspan="5">
          <p class="has-text-centered">
            <span id="month">
              {{ month() }}
            </span>
            <span id="year">
              {{ year() }}
            </span>
          </p>
        </th>
        <th>
          <span class="button next" @click="nextMonth">
            <i class="fas fa-angle-right"></i>
          </span>
        </th>
      </thead>
      <tbody id='updateCalendarForm'>
        <tr class="tr">
          <th>Lun</th>
          <th>Mar</th>
          <th>Mer</th>
          <th>Jeu</th>
          <th>Ven</th>
          <th>Sam</th>
          <th>Dim</th>
        </tr>
        <tr v-for="week in weeks">
          <td v-for="day in week" @click="select" :class="{ selected: isSelected() }"><span v-if="typeof(day) === 'object'" >{{ day.date() }} </span></td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>

import moment from 'moment'

export default {
  name: 'Calendar',
  data () {
    return {
      date: moment(),
      selected: moment(),
      weeks: []
    }
  },
  methods: {
    month () {
      return this.date.format('MMMM')
    },
    year () {
      return this.date.year()
    },
    nextMonth () {
      this.date = moment(this.date.add(1, 'month'))
      this.weeks = this.getWeeks()
    },
    previousMonth () {
      this.date = moment(this.date.subtract(1, 'month'))
      this.weeks = this.getWeeks()
    },
    getWeeks () {
      let weeks = []
      let firstMonthDay = this.date.date(1).clone()
      let lastMonthDay = this.date.endOf('month').clone()
      let day = firstMonthDay.clone()
      let isFirstWeek = true

      while (moment(day).isSameOrBefore(lastMonthDay)) {
        let currentDay = day.clone()
        let line = []
        let daysInWeek = 0

        /* A calendar line from Monday to Sunday */
        for (let i = 0; i < 7; i++) {
          if (moment(currentDay).isSameOrBefore(lastMonthDay)) {
            if (isFirstWeek && i >= firstMonthDay.weekday()) {
              line.push(currentDay.clone())
              currentDay.add(1, 'day')
              daysInWeek++
            } else if (isFirstWeek && i < firstMonthDay.weekday()) {
              console.log('Push empty cell')
              line.push('')
            }

            if (!isFirstWeek) {
              line.push(currentDay.clone())
              currentDay.add(1, 'day')
              daysInWeek++
            }
          } else {
            console.log('Push empty cell')
            line.push('')
          }
        }

        day.add(daysInWeek, 'day')
        console.log(line)
        weeks.push(line)
        isFirstWeek = false
      }
      return weeks
    },
    select (event) {
      let year = this.date.year()
      let month = this.date.month()
      let day = event.target.innerHTML
      this.selected = moment({ y: year, m: month, d: day })
    },
    isSelected (element) {
      console.log(element)
      return (this.date.date() == element.innerHTML)
    }
  },
  created: function () {
    this.weeks = this.getWeeks()
  }
}
</script>

<style>
td:empty {
  cursor: default;
}
td {
  cursor: pointer;
  text-align: center !important;
  vertical-align: middle !important;
  width: 58px;
  height: 58px;
  padding: 5px !important;
}
td:not(:empty):hover {
  background: rgba(0,0,0,0.5);
  border-radius: 50%;
  transition: background 0.2s;
}
.selected {
  background: rgba(0,0,0,0.5);
}
</style>

Ce que je veux

Ce que je cherche à faire, c'est de récupérer le contenu innerText de la cellule dans le v-for du template. En gros, le passer en paramètre de la méthode isSelected().
Ce que je cherche, c'est de savoir quel élement est sélectionné pour lui appliquer une classe particulière. Dans le code que j'ai mis plus haut, on voit que ce que j'ai essayé est faux et très nettement moche :P

Ce que j'obtiens

Pour le moment, j'ai essayé de passer this en espérant qu'il passerait l'élément du DOM concerné (la cellule <td v-for="day in week" @click="select"><span v-if="typeof(day) === 'object'" >{{ day.date() }} </span></td>) comme par magie mais ça n'est pas le cas.

Voila voila, j'espère que mon problème vous inspire ahah.

Merci beaucoup pour votre attention ;)

1 réponse


Alexandre Gérault
Auteur
Réponse acceptée

Forcément, maintenant que j'ai posté j'ai trouvé la solution à mon problème...

J'en profite pour donner un conseil assez trivial à mes camarades têtes en l'air : attendez au moins d'avoir dormi une nuit avant de poster à l'aide sur un forum... Parfois la solution est quasiment évidente. Bref, voici les quelques modifications apportées :

Simplification de la fonction select

select (event) {
      this.selectedDay = moment({
        y: this.date.year(),
        m: this.date.month(),
        d: event.target.innerText
      })
    }

Utilisation correcte de isSelected :

isSelected (day) {
  if (typeof (day) === 'object') {
    return this.selectedDay.date() === day.date()
  }
  return false
}

Et dans la partie template :

<td v-for="(day, index) in week" :key="index" @click="select" :class="{ active: isSelected(day) }">
  <span v-if="typeof(day) === 'object'" >
    {{ day.date() }}
  </span>
</td>

PS : Si quelqu'un a une idée de pourquoi cette méthode est fausse je veux bien une explication

isSelected (day) {
  if (typeof (day) === 'object') {
    return this.selectedDay.isSame(day, 'day')
  }
  return false
}

Je suis à votre écoute (j'utilise la bibliothèque moment.js)