Vue date-picker를 통한 캘린더 만들기

Heina·2022년 9월 23일
0
post-thumbnail

캘린더를 component하여 사용하기

기존 프로젝트에서는 한 페이지 안에 모든 데이터를 넣다보니 무겁고, 유지보수할 때 찾는게 쉽지 않았다.
그래서 신규 프로젝트때는 component화해서 잘 사용했으면 좋겠다는 생각이 들어서 시작되었다. 나의 지옥행이(?)

프로젝트에서 페이지마다 형태가 다르기때문에 본인 페이지에 맞게 커스텀이 필요했다. 공통component에 다 집어넣으면 사용이 쉽지 않기때문에!

그래서 제일먼저 하고자한것이 캘린더를 component화 시키는 것

1. 캘린더 형태 잡기 (Bootstrap 사용)


나는 이런형태에서 날짜 클릭시 캘린더가 보여졌으면 했다.

<b-button-group class="mx-1">
	<b-button @click="showCalendar" class="btn-cal" variant="transparent">
		{{ startDate }}
		<span>📅</span>
	</b-button>
	<b-button :disabled="true" variant="transparent" class="mx-2">~</b-button>
	<b-button @click="showCalendar" class="btn-cal" variant="transparent">
		{{ endDate }}
		<span>📅</span>
	</b-button>
</b-button-group>

해당 형태를 만들었고, click event를 넣어서 클릭시 캘린더가 보여지도록 했다.
startDateendDate는 day.js를 통해 생성하여 가져왔다.

<script>
import dayjs from 'dayjs'
export default {
  layout: 'default',
  name: 'MemberInfo',
  components: {
},
data() {
  return {
      calendarFlag: false,
      range: {
        start: dayjs().format('YYYY-MM-DD'),
        end: dayjs().format('YYYY-MM-DD'),
      },
},
computed: {
  startDate() {
    return this.range.start
  },
  endDate() {
    return this.range.end
  },
},
methods: {
  showCalendar() {
    this.calendarFlag = true
  },
  hideCalendar() {
    if (this.calendarFlag) this.calendarFlag = false
  },
},
</script>

그리고 show, hide 하는 methods를 작성해 놓는다!

2. 캘린더 component 생성 (v-date-piker 사용)

calendar.vue 생성 후 코드 작성하기

<template>
  <div>
    <v-date-picker
      :v-model="range"
      locale="ko"
      is-range
      mode="date"
      class="datetime-picker"
      is24hr
      :max-date="new Date()"
      :columns="
        $screens({
          default: 1,
        })
      "
    >
    </v-date-picker>
  </div>
</template>
<script>
export default {
  name: 'Calendar',
  data() {
    return {}
  },
  props: {
    range: {
      type: Object,
      default() {
        return {}
      },
    },
  },
}
</script>

<style>
.datetime-picker {
  position: absolute;
  left: 430px;
  top: 120px;
  z-index: 5;
}
</style>

3. 캘린더 작동하게 하기 ($edmit 사용하기)

이제 진행해야 할 것은
1) 캘린더(자식) component를 부모 component에 장착시키기
2) click 했을 때 오픈시키기
3) 캘린더 외 다른 부분을 클릭했을 때 닫게하기 (자식컴포넌트에서 작동으로 부모컴포넌트의 데이터 변경시키기)

캘린더(자식) component를 부모 component에 장착시키기

<Calendar v-if="calendarFlag" @hideCalendar="hideCalendar" :range="range" />

click 했을 때 오픈시키기

click했을 때 calendarFlag값이 true가 되도록 설정해 놓았다.

v-if="calendarFlag" calendarFlag가 true일경우 컴포넌트를 보여준다. 그리고 데이터(range)를 props로 전송한다.

자식컴포넌트에서 작동으로 부모컴포넌트의 데이터 변경시키기

자식컴포넌트에서 무언가를 작업하면 calendarFlag 값이 false가 되면서 캘린더가 닫혀야 한다

그러나! 부모에 있는 값을 자식이 함부로 가공해서는 안된다.
그렇기 때문에 자식이 부모한테 '이 값을 바꿔주세요~'라고 요청해야 한다

그래서 자식 component에서 $emit 쏴주자

자식 component 에서 보낼 정보를 $emit 통해서 신호 발송! this.$emit(’이벤트명’)

<template>
  <div>
    <v-date-picker
      v-click-outside="onClickOutside" 
      :v-model="range"
      locale="ko"
      is-range
      mode="date"
      class="datetime-picker"
      is24hr
      :max-date="new Date()"
      :columns="
        $screens({
          default: 1,
        })
      "
    >
    </v-date-picker>
  </div>
</template>
methods: {
    onClickOutside() {
      this.$emit('hideCalendar')
    },
  },

v-click-outsid은 vuetify에서 확인할 수 있는데, 이외의 것을 찍으면 특정 함수를 실행시켜주는것이다. 그래서 우리는 캘린더를 닫아달라는 이벤트명을 쏴주도록 한다.

부모 component 에서 받을 준비를 한다 @이벤트명=”실행할 함수”

<Calendar v-if="calendarFlag" @hideCalendar="hideCalendar" :range="range" />

그래서 이제 부모가 해당 값을 받고나면 hideCalendar()를 실행하여 닫게한다.

그러려고 했으나? 이렇게 쉽게 끝날리 없지

캘린더는 보이는데 닫히지가 않는다! 그래서 서치한 결과

directives: {
    clickOutside: {
      bind(el, binding, vnode) {
        el.event = function (event) {
          if (event.target.classList.contains('not-outside')) return false
          if (!(el === event.target || el.contains(event.target))) {
            vnode.context[binding.expression](event)
          }
        }
        document.body.addEventListener('click', el.event)
      },
      unbind(el) {
        document.body.removeEventListener('click', el.event)
      },
    },
  },

이것을 사용해야 한다고 한다.
그래서 당연히 넣어보았으나, 실행되지 않았고
console()로 찍어보았을때 캘린더는 쥐도새도 모르게 사라져있고 해당 함수마다 다 각각 실행되어 있었다.

뭔가 한 tick만 잡으면 보일것 같은데 계속 고민하고 ref를 사용해보려고 열심히 시간을 보내봤지만 되지 않았다. 그러다 캘린더 버튼을 @click.stop="showCalendar" click.stop 형태로 바꾸었더니 매우 아주 잘 실행되었다.

<b-button @click="showCalendar" class="btn-cal" variant="transparent">
		{{ startDate }}
  <span>📅</span>
</b-button>

그리고 결론!

2일에 걸려서 이것저것 알아보면서 하느라 성공시켰지만 V-Calendar를 쓰기로 했다. 기성품이좋아
V-Calendar

0개의 댓글