본문 바로가기

3.구현/HTML5&Javascript

[vue2] 목록에서 체크박스 선택 처리

vue.js에서 배열 데이터를 목록 또는 표 형태로 출력하는 방식은 매우 간편하다. 그러나, 출력된 목록에서 일부 데이터를 선택해서 처리하는 경우는 어려워보인다. 이 부분도 쉽게 처리 하는 방법이 있다.

들어가기

목록으로 출력하고 데이터 선택은 체크박스로 처리하는 예제이다. 저도 예전에는 상당히 복잡하게 만든 기억이 있다. 여기서는 최종 3가지 방식으로 처리하는 예를 살펴보겠다. 물론 다른 방식도 있을 수도 있지만, 제가 사용했던 것 중에 그나마 좋다고 생각하는걸 정리했다.

작성자: ospace114@empal.com, http://ospace.tistory.com/

간단한 목록 출력

처리할 데이터는 vue.js에 있는 가이드 문서를 참고해서 사용했다.

data() {
  return {
    todos: [
      { id:1, text: 'JavaScript 배우기' },
      { id:2, text: 'Vue 배우기' },
      { id:3, text: '무언가 멋진 것을 만들기' }
    ],
  };
},

리스트 태그를 사용해서 출력해보았다. 흔한 예제라서 크게 어려운게 없다.

<ol>
  <li v-for="(todo, index) in todos" :key="index">
    {{ todo.text }}
  </li>
</ol>

선택처리 방법1

아주 간단한 선택예제이다. 먼저 선택된 데이터를 저장할 selected 객체를 data에 추가한다.

data() {
  return {
    //...
    selected: [],
  };
},

체크박스 추가

체크박스를 추가하고 방금 추가한 selected와 바인드한다. 끝이다.

<ol>
  <li v-for="(todo, index) in todos" :key="index">
    <label>
      <input type="checkbox" :value="todo" v-model="selected"> {{ todo.text }}
    </label>
  </li>
</ol>

todos에서 선택된 객체가 selected에 추가되고, 체크박스에 값과 비교해서 selected에 있다면 체크표시하고 없다면 체크해제 한다.

모두 선택/해제

모든 데이터를 선택하고 해제하는 경우도 computed을 사용해서 처리하면 쉽다.
이번에는 All이라는 체크박스를 먼저 추가하자. 일단 all에 바인딩했다.

<label>
  <input type="checkbox" v-model="all"> All
</label>
<ol>
  <li v-for="(todo, index) in todos" :key="index">
    <label>
      <input type="checkbox" :value="todo" v-model="selected"> {{ todo.text }}
    </label>
  </li>
</ol>

이제 computed를 사용해서 all을 정의해보자.

computed: {
  all: {
    get() {
      return this.selected.length === this.todos.length;
    },
    set(val) {
      if (val) {
        this.selected = [...this.todos];
      } else {
        this.selected = [];
      }
    },
  },
}

선택여부는 get()을 통해서 가져오고 모두 선택/해제는 set()를 통해서 설정한다.
get()은 selected에 선택된 개수와 현재 표시하고 있는 데이터 개수를 비교해서 같다면 모두 선택되어 있고 다르며, 일부 선택안된 데이터가 있다는 의미이다.
set()도 val이 true이며 모두 선택이라는 의미로 todos에 모든 항목을 selected에 넣으면되고, false이면 모두 해제이므로 selected을 빈 배열로 넣어주면 된다.

선택처리 방법2

앞의 방식은 장점은 정말 단순하다. 단점도 있다. 객체로 선택 상태를 관리하기 때문에 같은 값이라도 객체가 달라지면 선택되지 않는다. 또한 선택한 데이터를 별도로 관리하고 있다고 목록 표시할 때에 선택 데이터 같이 표시하는 경우 매번 선택된 객체를 찾아서 넣어줘야하는 불편함이 있다.

이번 예제는 이런 부분을 약간 개선하였다. 각 데이터 식별자로 데이터를 유일하게 식별할 수 있어야 한다. 다행이 예제에는 각 todo에 id가 있어서 이를 사용해서 각 데이터 들을 식별할 수 있다.

체크박스 수정

먼저 선택하는 체크박스를 약간 수정하겠다. todos에 id를 사용했다.

<ol>
  <li v-for="(todo, index) in todos" :key="index">
    <label>
      <input type="checkbox" :value="todo.id" v-model="selected"> {{ todo.text }}
    </label>
  </li>
</ol>

매우 간단하다. selected에는 id가 저장되고, selected에 id 값으로 각 체크박스에서 자신의 값고 같은 경우 체크표시하게 된다.

모두 선택/해제

모든 데이터 선택/해제하는 체크박스는 수정이 필요하다. get()은 동일하지만, set()은 약간의 코딩을 해야한다.

computed: {
  all: {
    get() {
      return this.selected.length === this.todos.length;
    },
    set(val) {
      if (val) {
        this.selected = this.todos.map(it=>it.id);
      } else {
        this.selected = [];
      }
    },
  },
}

최대한 단순하게 처리했다.

선택처리 방법3

이번은 앞의 방법으로 처리 안되는 경우이다. selected도 없고 데이터 식별할 식별자도 없는 경우이다.
이럴 경우는 todos에 데이터 안에 선택여부 데이터를 포함하는 방법이다.

체크 박스 수정

각 데이터마다 checked라는 선택여부 데이터를 추가했다. 그리고 체크박스에 해당 checked로 바로 바인딩했다.

<ol>
  <li v-for="todo in todos" :key="todo.id">
    <label>
      <input type="checkbox" v-model="todo.checked"> {{ todo.text }}
    </label>
  </li>
</ol>

todos에서 각 데이터별로 checked을 사용해서 선택했는지 여부를 판단할 수 있다.

모두 선택/해제

모두 선택/해제 체크박스도 수정이 필요하다. 이번에는 기본 방법과 차이가 있기에 처리방식이 틀리다.

computed: {
  all: {
    get() {
      for(var i=0; i<this.todos.length; ++i) {
        if (!this.todos[i].checked) return false;
      }
      return true;
    },
    set(val) {
      this.todos.forEach(it=>it.checked = val);
    },
  }
}

get()에서는 todos 배열에서 checked가 하나라도 true가 아닌 경우는 모든 선택이 아니기 때문에 바로 false를 반환한다.
set()에서는 선택여부 값을 todos에 있는 모든 데이터의 checked에 저장하면 된다. 그러면 모두 선택 또는 해제할 수 있다.

결론

목록에서 체크박스를 사용해서 선택하는 부분과 모두 선택 또는 해제하는 체크박스를 선택하고 해제하는 방법을 살펴보았다. 지금까지 작업한 정수만을 정리하였다. 큰문제가 없다면 방법1이 가장 단순하고 빠르게 작업이 가능하다. 만약 선택된 데이터를 로딩시 표시해야한다면 방법2를 사용하는게 좋다. 선택된 데이터 식별자들을 미리 추출해 selelected에 넣어주면 된다. 이 방식도 안된다면 방법3을 사용하면 된다. 방법3은 조심하지 않으면 에러 가능성이 많이 때문에 주의가 필요하다.
그리고 더 좋은 방법이 있다면 저에게 알려주시면 감사하겠습니다. 도움이 되었으면하네요. 즐프하세요. ospace.

참조

[1] https://kr.vuejs.org/v2/guide/index.html#%EC%A1%B0%EA%B1%B4%EB%AC%B8%EA%B3%BC-%EB%B0%98%EB%B3%B5%EB%AC%B8
[2] https://kr.vuejs.org/v2/guide/list.html

반응형