본문 바로가기

Front-End/Vue.js

[Vue.js] 체크박스 선택 행 삭제, 행 추가 기능 만들기

1. 설명

1) 화면을 불러올때 행 데이터 배열을 Spring Back-End에서 조회한 뒤 화면에 뿌려주는 흐름입니다.

2) 추후 기존 originData는 DB에서 가져온 데이터로 표시하여 행이 삭제될때 DB도 함께 삭제되도록 구현하기위해 연관 코드가 존재합니다.

 

2. <template> </template> code

1) colList 내부의 row object 마다 v-model:value로 양방향 바인드하여 입력 받는 값이 바인딩 되도록 구현하였습니다.

2) fn_rowReadonly 함수는 아래 코드가 없어 기술합니다. 해당 object에 readOnly 속성에 따라 변경됩니다.

 

<tr v-for="(row, index) in colList" :key="`${row}` + index">
 <td class="text-center"> {{ index }}
   <CheckBox :id="index" :value="row.colIndex" v-model="checkedIndex" :key="index" />
 </td>
 <td class="text-start">
   <InputText v-model:value="row.COLUMN_NM" :readonly="fn_rowReadonly(row)" />
 </td>
 <td class="text-start">
   <InputText v-model:value="row.DATA_TYPE" :readonly="fn_rowReadonly(row)" />
  </td>
  <td class="text-end">
   <InputText v-model:value="row.DATA_SIZE" :readonly="fn_rowReadonly(row)" />
  </td>
  <td class="text-start">
   <InputText v-model:value="row.CMMNT" :readonly="fn_rowReadonly(row)" />
  </td>
  <td>
	<InputText v-model:value="row.RMK" />
  </td>
</tr>

 

3. 버튼 이벤트 code

<div class="row">
  <div class="btn_area float-end">
    <button type="button" class="btn secondary" @click="fn_addRow">
      <span class="txt">+ 행추가</span>
    </button>
    <button type="button" class="btn secondary" @click="fn_removeRow(0)">
      <span class="txt">- 행삭제</span>
    </button>
  </div>
</div>

 

4. data(){} code

ajax 통신을 이용한 데이터를 받아 저장할 배열 선언, 체크한 행 index를 담아둘 변수 선언

1) reactive() 로 배열을 정의해둔 이유는 배열에 요소를 추가하거나 제거할 때, Vue가 자동으로 변경사항을 감지하고 관련된 컴포넌트를 업데이트시키도록 하기 위함입니다.

data(){
    …

    colList: reactive([]),

    checkedIndex: [],

    …
}

 

5. methods: code

1) 선택한 배열의 인덱스를 역순으로 삭제하는 이유

     - [0] , [1] , [2], [3] 의 배열이 있다고 가정할 때  

     - 0, 2 인덱스 값 삭제 진행

     - 0을 삭제 하였을 경우 배열이 [1], [2], [3] 으로 변하게 되고 이때 인덱스 2의 값은 [2] -> [3] 으로 됩니다.

     - 뒤에서 부터 삭제하면 이와 같은 상황을 방지 할 수 있습니다.

methods: {
    async fn_removeRow() {

      this.crudMode = 'D';

      let indicesToRemove = await this.selIndicesToArr();

      //기존 사용 컬럼 목록 확인
      this.removeColList = await this.fn_originChk(indicesToRemove);
      if (0 < this.removeColList.length) {

		//DB Data 삭제 진행
        
      } else {
      
        //Front-End 삭제 진행
        indicesToRemove.forEach(index => {
          this.colList.splice(index, 1);
        });

        this.checkedIndex = []; // 선택된 행 초기화
      }
    },

    async selIndicesToArr() {
      let indicesToRemove = this.checkedIndex.map(checkedItem =>
        this.colList.findIndex(item => item.colIndex === checkedItem)
      );
      indicesToRemove.sort((a, b) => b - a); // 역순으로 정렬하여 뒤에서부터 삭제
      return indicesToRemove;
    },

    fn_rowReadonly(row) {
      return row.readonly !== null && row.readonly !== false
    },

    fn_addRow() {
      var colIndex = this.colList.length;
      this.colList.push({ "colIndex": colIndex, "readonly": false, "isOriginalData": false });
    },

	//컬럼 리스트 조회
    async fn_getColList(data) {
      const params = {
        tableNm: data
      };

      await axios.post(process.env.VUE_APP_API_URL+'/getData.do', params)
        .then(response => {
          var i = 0;
          this.colList = response.data.map(item => {
            var ret = { ...item, isOriginalData: true, colIndex: i };
            i++;
            return ret;
          });
        })
        .catch(error => {
          console.log('error >> ' + error)
        })
    },
    
    async fn_originChk(rmveChkArry) {

      var ret = [];
      rmveChkArry.forEach(index => {
        var item = this.colList[index];
        var orginChk = true;
        if (item.isOriginalData == null || typeof item.isOriginalData == "undefined") {
          orginChk = false;
        }

        if (item.isOriginalData && orginChk) {
          var col = this.colList[index];
          ret.push(col)
        }
      });

      return ret;
    },
}

 

6. computed: code

전체 선택, 전체 선택 해제 관련 코드

computed: {

    ...
    
    allSelected: {
      //getter
      get: function () {
        // 모든 `colList` 항목의 ID가 `checkedIndex`에 포함되어 있는지 확인
        return this.colList.every(row => this.checkedIndex.includes(row.colIndex));
      },
      set: function (value) {
        // `value`가 참이면 모든 항목의 ID를 `checkedIndex`에 할당, 거짓이면 빈 배열 할당
        this.checkedIndex = value ? this.colList.map(row => row.colIndex) : [];
      }
    },
  },

 

 

 

 


v-model:value와 v-model의 차이

v-model:value는 주로 Vue 3에서 컴포넌트에 사용될 때 명시적으로 prop 이름을 지정할 필요가 있을 때 사용됩니다. 예를 들어, 사용자 정의 컴포넌트에서 value prop을 다루는 경우 v-model 대신 v-model:value를 사용하여 명확하게 할 수 있습니다. 그러나 일반적인 HTML 폼 요소에서는 v-model만 사용해도 충분합니다. Vue 3에서는 v-model을 사용하여 다른 prop 이름에 대한 양방향 바인딩도 구현할 수 있으며, 이를 위해 v-model:propName 형식을 사용합니다. 따라서 예제의 v-model:value는 정해진 문법이 아니라 value라는 propName입니다! ㅎ

'Front-End > Vue.js' 카테고리의 다른 글

Chat GPT에게 물어본 watch의 deep옵션  (0) 2024.02.22
axios response undefind 문제  (0) 2024.02.22
Vue Warn 사례  (0) 2024.02.22