본문 바로가기

3.구현/HTML5&Javascript

[vue2] Vuetiful Datatable 사용하기

들어가기

테이블 형태의 데이터 표현은 매우 많이 사용되는 UI이다. 이런 Data Table 종류의 컨트롤들은 데이터를 테이블 형태로 자주 출력하고 테이블 형태로 데이터를 수정할 때 많이 사용한다. 이전에 사용 중인 element UI 라이브러리 중에 Table 컴포넌트가 있었는데 단순 데이터 표현에는 적합하지만 편집 기능이 없었다. 그래서 대안으로 찾은게 Vuetiful 라이브러리에서 DataTable이다. 혹시나해서 Vuetify UI 라이브러리가 아니라 Vuetiful이다.

Vuetiful은 Vuejs용 컴포넌트 라이브러리로서 Calendar, Chip, Datatable, Datetime-picker, Dynamic, Floating-Panel, Paginator, Panel, Tab-control, Toggles 같은 컴포넌트를 제공하고 있다.

이제 Vuetiful Datatable에 대해서 알아보자.

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

Demo

See the Pen Vuetiful datatables by ospace (@ospace) on CodePen.

 

환경 구성

거창하게 node.js까지 구성하면서 진행하기는 복잡하기 때문에 바로 Visual Sudio Code같은 텍스트 편집기에서 바로 작업할 수 있게 환경 구성을 할려고한다. 물론 node.js을 구성해서 vuejs 프로젝트를 생성해서 작업해도 된다. 먼저 사용할 HTML 파일을 생성해보자.

Vuetiful UI 라이브러리 CSS 파일을 로딩한다.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/andrewcourtice/vuetiful@3bea2aa/dist/components/app.style.css">

Vue2와 Vuetiful UI 라이브러리인 javascript 파일을 로딩한다. date_fns 라이브러리는 vuetiful UI 라이브러리에 필수이기 때문에 반드시 포함시켜야 한다.

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
<script src="http://cdn.date-fns.org/v1.0.0/date_fns.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/andrewcourtice/vuetiful@3bea2aa/dist/components/components.bundle.js"></script>

사용할 기본 Vue 화면 템플릿이다. id가 “app”인 div 태그 하나이고 이를 가지고 Vue 객체를 생성한다.

<div id="app"> </div>
<script type="text/javascript">
  new Vue({
    el: '#app',
    data() { },
  });
</script>

사용할 기본 데이터이다. 현재는 간단하게 추가했지만, 필요하면 데이터를 더 추가하면 된다.

[
  { id:1, enable:true, name: "Nancy Fuller", email:"nfuller0@about.me", amount:1166.14 },
  { id:2, enable:false, name: "Melissa Meyer", email:"mmeyer1@angelfire.com", amount:6123.50 },
  { id:3, enable:true, name: "Larry Rose", email:"lrose2@cdbaby.com", amount:8288.27 }
]

최종 소스 코드

앞의 내용을 모두 반영한 최종 소스코드이다. 앞으로는 이 코드를 기반으로 작업할 예정이다.

<html>
  <head>
    <title>Vuetiful Datatable</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/andrewcourtice/vuetiful@3bea2aa/dist/components/app.style.css">
  </head>
  <body>
    <div id="app"> </div>
    <script type="text/javascript">
      new Vue({
        el: '#app',
        data() {
          table_data: [
            { id:1, enable:true, name: "Nancy Fuller", email:"nfuller0@about.me", amount:1166.14 },
            { id:2, enable:false, name: "Melissa Meyer", email:"mmeyer1@angelfire.com", amount:6123.50 },
            { id:3, enable:true, name: "Larry Rose", email:"lrose2@cdbaby.com", amount:8288.27 }
          ]
        },
      });
    </script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
    <script src="http://cdn.date-fns.org/v1.0.0/date_fns.min.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/andrewcourtice/vuetiful@3bea2aa/dist/components/components.bundle.js"></script>
  </body>
</html>

Datatable 기본 생성

먼저 Datatalbe의 기본 컴포넌트를 생성해보자.

HTML

datatable이 사용할 UI 컴포넌트이다.

<datatable :source="table_data">
<datatable-column
    v-for="col in table_columns" :key="col.id"
    :id="col.id" :label="col.label" :width="col.width">
</datatable-column>
</datatable>

source 속성에 사용할 데이터 객체를 넘겨주면 된다. datatable-column을 사용해 테이블 컬럼을 설정해준다.

  • id: 컬럼 식별 정보이고 추후 데이터에서 값을 추출할 때 사용할 키이다.
  • label: 컬럼에 표시되는 이름이다. 없다면 id가 사용된다.
  • width: 컬럼 폭을 지정한다. 단위는 기본은 %이고 다른 단위를 지정할 수 있다.

컬럼 정보를 table_columns에 별도로 두고 컬럼 정보를 가지고 v-for로 순회하면서 datatable-column을 생성하고 있다. 이렇게 하면 컬럼을 동적으로 관리할 수있다.

Javascript

데이터에 table_columns 배열을 추가한다.

data(): {
  return {
    table_columns:[
      {id:'name', label:'이름', width: 20},
      {id:'email', label:'이메일', width: 50},
      {id:'amount', label:'금액', width: 30},
    ],
    // ...
  }
}

사용할 키는 id, label, width로 컬림 식별 이름, 컬럼 이름, 컬럼 폭을 의미한다.

에러가 없다면 아래와 같은 화면이 보인다.

Fig 01. 기본 화면

보여지는 기본 테이블 디자인이 깔끔하다. 상단에 헤더가 보이고 중간에 데이터가 출력된다. 아래쪽에서 데이터 검색 입력 창이 있다. 차체적으로 기본적인 검색 기능도 가능하다.

컬럼 데이터 수정

테이블에 출력된 데이터를 수정하는 방법을 살펴보자. 만약 직접 테이블의 값을 수정하는 기능을 구현하려면 까다롭다. Datatable에서 이런 작업이 매우쉽다.

HTML

datatable 컴포넌트의 속성 중에 editable이 있다. 이 속성을 true로 해주면 간단하게 수정 작업이 가능하게 된다.

<datatable :source="table_data" :editable=true :line-numbers=true>
<datatable-column
    v-for="column in table_columns" :key="column.id"
    :id="column.id" :label="column.label" :width="column.width">
</datatable-column>
</datatable>

추가로 line-numbers 속성을 true로 해주면 테이블 앞에 순서 번호가 표시된다. 바로 수정할 데이터를 선택해서 값을 변경할 수 있다.

새로운 데이터 추가 및 삭제

이번에는 새로운 데이터를 추가해보다. 그리고 기존 데이터를 삭제하는 기능도 살펴보자. 새로운 데이터 추가는 별도 화면을 만들어서 추가하기 보다는 테이블에 로우을 하나 추가해서 바로 입력하는 형태로 진행할려고 한다.

추가 버튼 HTML

먼저 새로운 데이터를 추가위한 ADD 버튼을 datatalbe 컴포넌트 이전에 추가하자.

<button @click="addColumn">ADD</button>
<datatable> ... </datatalbe>

ADD 버튼에 클릭 이벤트에 addColumn 메소드를 지정한다.

추가 버튼 Javascript

앞에 ADD 버튼에 지정한 addColumn 메소드를 정의해보자. addColumn 메소드는 methods에 추가하면 된다.

methods: {
  addColumn: function() {
    var td = this.$data.table_data;
    var id = (0==td.length) ? 0 : td[td.length-1].id+1;
    td.unshift({id: id, name:'', email:'', amount:0});
  }
  // ...
}

별다른 부분은 없다. 현재 table_data을 가져오고 table_data에 기본 데이터 객체를 추가하면 된다.

삭제버튼 HTML

다음으로 삭제버튼을 추가해보자. 삭제는 컬럼 하나를 마지막에 추가하고 삭제 버튼을 배치한다. 그리고 삭제 버튼을 클릭할 경우 해당 로우에 있는 데이터를 삭제하도록 구현할려고 한다.

기존 datatable-column뒤에 새로운 datatable-column 컴포넌트를 배치한다.

<datatable …>
<datatable-column id="actions" label="작업" width="10">
</datatable-column>
<template slot="actions" scope="cell">
  <button @click="delColumn(cell.row)">삭제</button>
</template>
</datatable>

id는 actions으로 지정하고 label은 “작업”으로 설정했다. 그리고 별도 슬롯을 만들고 slot을 id와 동일한 actions으로 지정한다. 그러면 datatable-column에 id와 동일한 슬롯을 매핑해 해당 셀에 출력하게 된다.

슬롯에 버튼을 추가하고 클릭 이벤트에 delColumn 메소드를 지정한다. 그리고 메소드 인자는 스코프에 cell에서 현재 데이터의 레코드 값를 넘겨줘서 레코드 값을 가지고 원하는 데이터를 삭제하면 된다.

삭제버튼 Javascript

삭제 버튼을 클릭할 경우 호출될 delColumn 메소드를 methods 안에 정의하면 된다.

methods: {
  delColumn: function(col) {
    var table_data = this.$data.table_data;
    var idx = table_data.indexOf(col);
    0 <= idx && table_data.splice(idx, 1);
  }
  // ...
}

받아온 레코드 값인 col을 가지고 현재 table_data에서 인덱스 값을 가져온다. 인덱스로 해당 데이터를 배열에서 삭제하면 된다.

아래는 결과 화면이다. 삭제 버튼을 클릭하면 해당 데이터가 삭제되서 보이지 않게 된다.

Fig 02. 레코드 추가 및 삭제

선택 삭제

삭제할 데이터가 많을 경우 매번 삭제 버튼을 클릭해서 삭제하기 쉽지 않다. 체크박스를 사용한 삭제을 살펴보자. 테이블 각 로우에 체크박스를 표시하고 체크박스에 체크한 데이터들을 삭제 버튼 한번으로 모두 삭제하는 기능이다.

HTML

먼저 선택 데이터를 삭제하기 위한 삭제 버튼을 추가하고 테이블 앞쪽에 체크박스 표시할 컬럼을 추가한다.

<button @click="addColumn">ADD</button>
<button @click="delColumnSelected">DEL</button>
<datatable ...>
 <datatable-column id="sel" label="sel">
    <checkbox id="sel-all" v-model="selectAll">
    </checkbox>
  </datatable-column>
  ...
  <template slot="sel" scope="cell">
    <checkbox :id="'sel-'+cell.row.id"
     :value="cell.row" v-model="selected"></checkbox>
  </template>
</datatable>

DEL 버튼은 ADD 버튼 다음에 추가하면 된다. 그리고 datatable 컴포넌트에서 앞쪽에 datatable-column을 추가하고 id를 sel로 label을 “sel”로 한다. datatable-column에 안에 체크 박스를 추가한다. 이 체크 박스를 헤더에 표시되고 체크할 경우 테이블에 표시된 모든 체크박스를 선택한다. 물론 기본적으로 지원하지 않기에 구현해줘야 한다. 그래서 체크박스와 바인딩할 selectAll 객체를 지정한다. datatable-column 안에 있는 체크박스를 헤더에 표시되고 슬롯은 테이블 바디에 표시된다.

앞에 컬럼에 삭제 버튼 추가한 경우처럼 슬롯을 추가하고 slot을 id와 동일한 sel로 한다. 그리고 checkbox 컴포넌트를 추가한다. id는 “sel-”와 각 데이터 id를 조합해서 설정하고 value은 레코드을 지정한다. 그리고 체크박스 여부를 위해 selected와 바인딩한다.

기능적으로 단순해보이지만 해야할 작업이 좀 있다. 이제 추가로 스크립트 작성을 하면 된다.

Javascript

앞에서 삭제 버튼을 위한 delColumnSelected 메소드와 헤더에 있는 체크박스에 바인딩된 selectAll과 각 로우에 표시된 체크박스와 바인딩된 selected을 정의해주면 된다.

data() {
  return {
    selected: [], 
    // ...
  };
},
computed: {
  selectAll: {
    get: function () {
      return this.$data.selected.length
             == this.$data.table_data.length;
    },
    set: function (val) {
      this.$data.selected =
        val ? this.$data.table_data : [];
    }
  }
},
methods:{
 delColumnSelected: function() {
  this.$data.selected.forEach(it=>this.delColumn(it));
 }
}

data에 selected을 추가하고 빈 배열 객체를 할당한다. 현재 선택된 레코드들이 저장된다. 체크박스의 데이터가 selected에 있다면 체크표시된다.

selectAll을 data에 추가하지 않고 computed에 추가한다. 그리고 get에는 모든 데이터가 선택되어 있다면 체크 표시하게 한다. 만약 체크박스 버튼을 클릭해서 값이 변경된다면 set에 의해서 현재 선택된 데이터를 모두 해제하거나 모두 추가한다.

delColumnSelected 메소드로 현재 선택된 데이터를 기준으로 기존 delColumn 메소드를 사용해 삭제한다.

토글버튼 사용

이번에는 데이터의 레코드에 bool 값이 있을 경우 화면에 텍스트나 숫자로 표현하기 보다 토글버튼을 추가해서 표시할려고 한다.

HTML

먼저 toggle 컴포넌트를 추가한다. toggle 컴포넌트 위치는 체크박스 다음에 추가한다.

<datatable ...>
  <datatable-column id="enable" label="enable">
    <toggle id="enable-all“
             v-model="enableAll"></toggle>
  </datatable-column>
  <template slot="enable" scope="cell">
      <toggle :id="'tgl-'+cell.row.id“
       v-model="cell.row.enable"></toggle>
  </template>
  ...
</datatable>

내용은 체크박스와 거의 동일하기 때문에 생략한다. 헤더에 있는 토글 버튼은 enableAll에 바인딩되고 바디에 있는 토글 버튼은 데이터에 enable 값과 바인딩된다.

Javascript

바인딩될 값 중에 enableAll과 enable은 있는데, enable은 이미 샘플 데이터에 추가되어 있는 상태이다. 새로 작업할 대상은 enableAll만 하면 된다.

computed: {
  enableAll: {
    get: function () {
      var dt = this.$data.table_data;
      return dt.map(it=>it.enable?1:0)
             .reduce((a,b)=>a+b,0)
             == dt.length;
    },
    set: function (val) {
      this.$data.table_data.forEach(it=>{
        it.enable=val;
      });
    }
  }
}

체크박스의 selectAll과 동일하다. 토글에 값을 가져올 때에는 모든 데이터의 enable이 true인지 확인하고 맞다면 true를 리턴한다. 그리고 토글을 클릭해서 값을 변경할 경우 모든 데이터의 enable 값을 변경한다.

페이징 처리

다음으로 테이블 페이징 처리를 보자. 데이터 많을 경우 한화면에 표시되지 않기 때문에 페이징 처리가 필요하다. Datatable에서 페이징 처리하는 방식을 살펴보자.

HTML

먼저 paginator 컴포넌트를 최상단 루트에 추가한다. 그리고 그 밑에 datatable 컴포넌트를 위치한다.

<paginator :source="table_data" :page-size=2>
  <template scope="page">
    <datatable :source="page.data" :editable=true :line-numbers=true>
        ...
    </datatable>
  </template>
</paginator>

datatable 컴포넌트에서 데이터 접근이 달라진다. 데이터를 paginator 컴포넌트를 거쳐서 받게 된다. 그래서 화면에 표시될 데이터 개수만 받게 된다. scope로 datatable로 전달할 이름을 지정한다. 그리고 datatable 컴포넌트에서 source 속성에 page의 data 객체를 받는다. 그리고 이것으로 끝이다. 화면 하단에는 페이지 네이게이터가 표시된다.

데이터 포멧팅

화면에 표시되는 데이터와 원본 데이터가 항상 동일하지 않는 경우가 많다. 날짜나 숫자 같은 경우 표시형식이나 포멧이 달라질 가능성이 높다. 원본 데이터를 포멧팅하는 방법을 살펴보자.

HTML

datatable-column 컴포넌트에 formatter 속성에 포멧팅할 메소드를 넘겨주면 된다.

<datatable :source="page.data" :editable=false :line-numbers=true>
  <datatable-column v-for="col in table_columns"
    :key="col.id"
    :id="col.id"
    :label="col.label"
    :width="col.width"
    :formatter="col.formatter">
  </datatable-column>
  ...
</datatable>

포멧팅 메소드는 컬럼 정보 데이터에서 formatter에 있는 값을 할당한다.

Javascript

table_columns 컬럼 정보 객체에 컬럼 항목에 formatter을 추가하고 사용할 포메팅 메소드를 지정한다.

data() {
  return {
    table_columns:[
      // ...
      {id:'amount', label:'금액', width: 20, formatter: this.fmtCurrency}
    ],
    // ...
  };
},
methods: {
  function fmtCurrency(val) {
    var currency = parseFloat(val);
    if(isNaN(currency)) return val;
    return "$"+currency.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, "$1,");
  },
  // ...
}

별다른 문제가 없다면 아래 화면 처럼 금액이 포메팅되서 출력된다. 주의할 부분은 앞에서 editable을 ture로 해서 편집 가능 상태인 경우는 포멧팅이 되지 않는다. 반드시 editable을 false로 해줘야 포멧팅이 된다.

Fig 03. 컬럼 포멧팅

Codepen

https://codepen.io/ospace/pen/KKEXgOb

결론

Vuetiful UI 라이브러리는 가볍게 사용하기에 좋다. 그리고 테이블 편집기능이 있는 간단한 처리할 수 있어서 유용하다.  아쉬운 점은 해당 프로젝트는 2011년 11월 09일을 마지막 Commit으로 중지되었다.  그것도 readme.md 수정 커밋이다.  그렇기에 추가적인 기능 개발을 기대하기 어렵습니다. 그렇기에 그 점을 감안해서 사용하기 바랍니다.

Vuetiful의 Datatable에서 소개되는 특징으로 정렬, 다중 컬럼 그룹핑, 필터링, 실시간 수정, 사용자 필터/포멧터 지원, 사용자 집합함수(min, max, total 등), 사용자 컬럼 헤더 템플릿, 사용자 셀 템플릿(보기 및 수정 모드)이 있다. 그리고 Vuetiful UI 라이브러리에는 다양한 컴포넌트들 있으니 상세한 내용은 참고 [1]을 보시면 된다.

vue2도 vue3로 넘어가는 추세이고 vue2 eol을 선언했지만 아직도 vue2를 사용하고 있기 때문에 참고용으로 남긴다. 혹시나 도움이 되었다면 다행이네요. 모두 즐거운 코딩생활 하세요. ^^ ospace.

참고

[1] andrewcourtice, Vuetiful Datatable, https://github.com/andrewcourtice/vuetiful/tree/master/src/components/datatable

[2] andrewcourtice, Vue.JS 2 - Advanced Datatable Component, https://codepen.io/andrewcourtice/full/woQzpa

[3] ospace, Vuetiful Datatable example, https://codepen.io/ospace/pen/KKEXgOb

반응형