✅ Vue로 체크박스 연속체크 구현하기

seoyeonpp·2024년 3월 14일
4

Vue.js

목록 보기
4/5
post-thumbnail

체크박스를 Shift 키 누른채로 클릭 시 해당 범위가 다 체크되도록 해달라는 요구사항이 있었다.

일단 지금 프로젝트는 Nuxt3으로 구현되어있고, 체크박스는 Nuxt UI의 table 컴포넌트에서 각 row 마다 있는 체크박스였다.

Nuxt UI의 table 컴포넌트는 v-model을 써서 체크박스를 클릭한 아이템을 배열로 받을 수 있게 되어있다!

그리고 Vue의 watch를 활용해서 체크박스의 개수가 변한것을 감지할 수 있다.

더 좋은 방법도 있겠지만, 나는 이렇게 구현했다.

1. keyDown, keyUp 이벤트 감지하여 Shift키 누르는 부분 감지

  • Vue 라이프사이클의 onMounted() 훅을 사용하여 이벤트를 추가했다.
  • 그리고 ref변수를 하나 선언하여 shift를 눌렀는지 아닌지 Boolean 값을 넣어줬다. (기본값은 false)
const isShiftKeyDown = ref(false); //변수선언

onMounted(() => {
  document.addEventListener('keydown', (event) => {
    if (event.key === 'Shift') {
      isShiftKeyDown.value = true;
    }
  });

  document.addEventListener('keyup', (event) => {
    if (event.key === 'Shift') {
      isShiftKeyDown.value = false;
    }
  });
});

2. 가장 최근에 누른 체크박스 id값을 얻기 위한 배열 선언

  • 내가 Shift를 누르기 전에 클릭했던 체크박스 값을 기억하기 위해 배열을 선언했다.
  • 해당 배열은 2개의 값만 가질 수 있다. (이전 체크박스 id값, 방금 클릭한 체크박스 id값)
const shiftCheckedArray = ref([]);

3. watch 이벤트 등록

  • 제일 중요한 이벤트 감지를 위해 watch를 사용했다!
  • watch의 to,from (현재,이전) 값을 활용해서 shift를 누르고 체크박스 선택 / 취소가 가능하도록 했다.
  • 현재 해당 코드가 작성된 컴포넌트는 list를 props로 받고있다.
  • 해당 list는 id들이 내림차순으로 나열되어있어서 sort를 이용하여 index를 맞췄다.
watch(
  () => checkbox.value,
  (to, from) => {
    if(checkbox.value !== null && checkbox.value.length > 0) {
      const toArr = checkbox.value?.map(item => item.id);
      const fromArr = from.map(item => item.id);
      const toSubtract = toArr.filter(item => !fromArr.includes(item));
      const fromSubstract = fromArr.filter(item => !toArr.includes(item));
      const currentClick = toSubtract.length > 0 ? toSubtract : fromSubstract;
  
      shiftCheckedArray.value.push(...currentClick);
  
  // 해당 배열은 2개의 값만 가질 수 있다. (이전 체크박스 id값, 방금 클릭한 체크박스 id값)
      if(shiftCheckedArray.value.length > 2) {
        shiftCheckedArray.value = shiftCheckedArray.value.slice(-2);
      }
  
  	// Shift 키 눌렀을때 로직
      if (isShiftKeyDown.value) {
        to.sort((a, b) => b - a);
        from.sort((a, b) => b - a);
  
        // Shift 로 체크 추가
        if(to && from &&to.length > 0 && from.length > 0) {
          const oldValue = props.list.findIndex((item) => item.id === from[from.length - 1]?.id);
          const newValue = props.list.findIndex((item) => item.id === to[to.length - 1]?.id);
      
            if (oldValue !== -1 && newValue !== -1) {
                for (let i = (newValue < oldValue ? newValue: oldValue); i < (newValue > oldValue ? newValue + 1 : oldValue + 1); i++) {
                  const item = checkbox.value.findIndex((item) => item.id === props.list[i].id);
      
                  if (item === -1) {
                    checkbox.value.push(props.list[i]);
                    checkbox.value.sort((a, b) => b.id - a.id);
                  }
                }
            }
        }
  
        // Shift 로 체크 제거
        if(fromSubstract.length > 0) {
          const oldValue = props.list.findIndex((item) => item.id === shiftCheckedArray.value[0]);
          const newValue = props.list.findIndex((item) => item.id === shiftCheckedArray.value[1]);
  
          for (let i = (newValue < oldValue ? newValue: oldValue); i < (newValue > oldValue ? newValue + 1 : oldValue + 1); i++) {
            const item = checkbox.value.findIndex((item) => item.id === props.list[i].id);
            if(item > -1) {
              checkbox.value.splice(item, 1);
            }
          }
        }
      }
    }
  }
);

후기

처음엔 쉬운줄 알았는데 하다보니까 머리를 쥐어뜯으면서 했다 🥲
그래도 어디 참고하지않고 내힘으로 개발한 기능이라 너무 뿌듯해서 블로그에 기록용으로 남겨둔당!
미래의 내가보면 뿌듯하겠징,,,

끝~!

0개의 댓글