Dandy Now!
  • [개발자의품격][부트캠프][1기][20차시] Vue.js #7 | 재사용 컴퍼넌트에 checkbox, radio button, custom event 적용 등
    2022년 03월 10일 00시 23분 07초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    checkbox, radio button 적용

    19차시 #6에서 작성한 코드에 checkbox(또는 radio button)를 추가하였다. 아래의 코드는 자식 component의 checkbox에 체크된 정보를 부모 component에서는 알지 못하는 상태이다. 부모 component에서 체크된 내용을 알 수 있어야 삭제, 수정 등의 기능을 적용할 수 있다.

     

    부모 component

    <!-- src/views/4_reuse/ListView.vue  -->
    <template>
      <div>
        <button class="btn btn-danger" @click="doSearch">조회</button>
        <!-- selectType는 radio를 문자열 그대로 넘길 것이기 때문에 데이터바인딩이 불필요하다. -->
        <simple-grid
          :headers="headers"
          :items="drinkList"
          selectType="checkbox"
          ckeckedKey="drinkId"
        />
      </div>
    </template>
    <script>
    // @를 사용하면 vue파일 경로가 변경되더라도 에러가 발생하지 않는다.
    import SimpleGrid from '@/components/fragments/SimpleGrid.vue'
    export default {
      components: { SimpleGrid },
      data() {
        return {
          // SimpleGrid.vue에 thead 정보를 전달
          headers: [
            { title: '제품번호', key: 'drinkId' },
            { title: '제품명', key: 'drinkName' },
            { title: '가격', key: 'price' }
          ],
          drinkList: []
        }
      },
      methods: {
        doSearch() {
          // SimpleGrid.vue에 tbody 정보를 전달
          this.drinkList = [
            {
              drinkId: '1',
              drinkName: '코카콜라',
              price: 700,
              qty: 1
            },
            {
              drinkId: '2',
              drinkName: '오렌지주스',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '3',
              drinkName: '커피',
              price: 500,
              qty: 1
            },
            {
              drinkId: '4',
              drinkName: '물',
              price: 700,
              qty: 1
            },
            {
              drinkId: '5',
              drinkName: '보리차',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '6',
              drinkName: '포카리',
              price: 1000,
              qty: 1
            },
            {
              drinkId: '7',
              drinkName: '뽀로로',
              price: 1300,
              qty: 1
            }
          ]
        }
      }
    }
    </script>

     

    자식 component

    <!-- src/components/fragments/SimpleGrid.vue  -->
    <template>
      <table class="table table-bordered">
        <thead>
          <tr>
            <!-- radio, checkbox 컬럼의 헤드를 만든다. -->
            <th v-if="selectType === 'radio' || selectType === 'checkbox'"></th>
            <th :key="th.key" v-for="th in headers">{{ th.title }}</th>
          </tr>
        </thead>
        <tbody>
          <tr :key="i" v-for="(item, i) in items">
            <!-- radio 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'radio'">
              <!-- radio 버튼을 checkedItem으로 그룹핑한다. -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <input
                type="radio"
                name=""
                id=""
                v-model="checkedItem"
                @change="doSelect"
              />
            </td>
            <!-- checkbox 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'checkbox'">
              <!-- checkbox 버튼을 checkedItems으로 그룹핑한다.  -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <!-- value로 유일한 Key값인 drinkId를 데이터바인딩, 어떤 row가 선택되었는지 알 수 있다. -->
              <input
                type="checkbox"
                name=""
                id=""
                :value="item[ckeckedKey]"
                v-model="checkedItems"
                @change="doSelect"
              />
            </td>
            <td :key="th.key" v-for="th in headers">{{ item[th.key] }}</td>
          </tr>
        </tbody>
      </table>
    </template>
    <script>
    export default {
      components: {},
      // props에서 부모 component로 부터 데이터를 받는다.
      props: {
        headers: {
          type: Array,
          default: function () {
            return []
          }
        },
        items: {
          type: Array,
          default: function () {
            return []
          }
        },
        // 부모 component로 부터 radio, checkbox 문자열을 받는다.
        selectType: {
          type: String,
          default: ''
        },
        // 부모 component로 부터 drinkId를 받는다.
        ckeckedKey: {
          type: String,
          default: ''
        }
      },
      data() {
        return {
          // radio 버튼 데이터바인딩
          checkedItem: '',
          // checkbox 버튼 데이터바인딩
          checkedItems: []
        }
      },
      methods: {
        // radio, checkbox 버튼 이벤트의 실행 함수
        doSelect() {
          if (this.selectType === 'radio') {
            console.log(this.checkedItem)
          } else if (this.selectType === 'checkbox') {
            console.log(this.checkedItems)
          }
        }
      }
    }
    </script>

     

    [그림 1] checkbox 적용

     


     

    custom event 적용

    부모 component

    부모 component인 listView.vue의 simple-grid에 커스텀 이벤트 @change-item를 작성한다. 이 이벤트는 changeCheckedValue 함수를 호출하도록 한다.

    <simple-grid
      ...
      @change-item="changeCheckedValue"
      />

     

    methods에 자식 component의 data를 인자로 받는 changeCheckedValue 함수를 작성한다.

    methods: {
        ...
        changeCheckedValue(data) {
          console.log('부모 component : ', data)
        }
      }

     

    ListView.vue 전체 코드

    더보기
    <!-- src/views/4_reuse/ListView.vue  -->
    <template>
      <div>
        <button class="btn btn-danger" @click="doSearch">조회</button>
        <!-- selectType는 radio를 문자열 그대로 넘길 것이기 때문에 데이터바인딩이 불필요하다. -->
        <!-- simple-grid 내에 커스텀 이벤트(@change-item)를 만들 수 있다. -->
        <simple-grid
          :headers="headers"
          :items="drinkList"
          selectType="checkbox"
          ckeckedKey="drinkId"
          @change-item="changeCheckedValue"
        />
      </div>
    </template>
    <script>
    // @를 사용하면 vue파일 경로가 변경되더라도 에러가 발생하지 않는다.
    import SimpleGrid from '@/components/fragments/SimpleGrid.vue'
    export default {
      components: { SimpleGrid },
      data() {
        return {
          // SimpleGrid.vue에 thead 정보를 전달
          headers: [
            { title: '제품번호', key: 'drinkId' },
            { title: '제품명', key: 'drinkName' },
            { title: '가격', key: 'price' }
          ],
          drinkList: []
        }
      },
      methods: {
        doSearch() {
          // SimpleGrid.vue에 tbody 정보를 전달
          this.drinkList = [
            {
              drinkId: '1',
              drinkName: '코카콜라',
              price: 700,
              qty: 1
            },
            {
              drinkId: '2',
              drinkName: '오렌지주스',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '3',
              drinkName: '커피',
              price: 500,
              qty: 1
            },
            {
              drinkId: '4',
              drinkName: '물',
              price: 700,
              qty: 1
            },
            {
              drinkId: '5',
              drinkName: '보리차',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '6',
              drinkName: '포카리',
              price: 1000,
              qty: 1
            },
            {
              drinkId: '7',
              drinkName: '뽀로로',
              price: 1300,
              qty: 1
            }
          ]
        },
        // 파라미터에 자식 component의 data를 넣는다.
        changeCheckedValue(data) {
          console.log('부모 component : ', data)
        }
      }
    }
    </script>

     

    자식 component

    자식 component인 SimpleGrid.vue의 methods 내 doSelect 함수의 checkbox에 $emit을 추가한다. 이렇게 하면 자식 component의 데이터(checkedItems)를 부모 component의 changeCheckedValue 함수에서 받을 수 있다. 이러한 logic을 이용해 자식 componet에서 선택한 row를 부모 component에서 수정, 삭제하는 기능을 적용할 수 있다. 수정한 코드의 실행 결과는 [그림 1]과 동일하다.

    methods: {
        // radio, checkbox 버튼 이벤트의 실행 함수
        doSelect() {
          if (this.selectType === 'radio') {
            console.log(this.checkedItem)
          } else if (this.selectType === 'checkbox') {
            // console.log(this.checkedItems)
            
            // $emit은 부모 component의 이벤트를 실행 시켜주는 글로벌 함수
            // $emit의 첫 번째 파라미터는 이벤트명, 두 번째는 데이터 입력
            this.$emit('change-item', this.checkedItems)
          }
        }

     

    SimpleGrid.vue 전체 코드

    더보기
    <!-- src/components/fragments/SimpleGrid.vue  -->
    <template>
      <table class="table table-bordered">
        <thead>
          <tr>
            <!-- radio, checkbox 컬럼의 헤드를 만든다. -->
            <th v-if="selectType === 'radio' || selectType === 'checkbox'"></th>
            <th :key="th.key" v-for="th in headers">{{ th.title }}</th>
          </tr>
        </thead>
        <tbody>
          <tr :key="i" v-for="(item, i) in items">
            <!-- radio 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'radio'">
              <!-- radio 버튼을 checkedItem으로 그룹핑한다. -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <input
                type="radio"
                name=""
                id=""
                v-model="checkedItem"
                @change="doSelect"
              />
            </td>
            <!-- checkbox 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'checkbox'">
              <!-- checkbox 버튼을 checkedItems으로 그룹핑한다.  -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <!-- value로 유일한 Key값인 drinkId를 데이터바인딩, 어떤 row가 선택되었는지 알 수 있다. -->
              <input
                type="checkbox"
                name=""
                id=""
                :value="item[ckeckedKey]"
                v-model="checkedItems"
                @change="doSelect"
              />
            </td>
            <td :key="th.key" v-for="th in headers">{{ item[th.key] }}</td>
          </tr>
        </tbody>
      </table>
    </template>
    <script>
    export default {
      components: {},
      // props에서 부모 component로 부터 데이터를 받는다.
      props: {
        headers: {
          type: Array,
          default: function () {
            return []
          }
        },
        items: {
          type: Array,
          default: function () {
            return []
          }
        },
        // 부모 component로 부터 radio, checkbox 문자열을 받는다.
        selectType: {
          type: String,
          default: ''
        },
        // 부모 component로 부터 drinkId를 받는다.
        ckeckedKey: {
          type: String,
          default: ''
        }
      },
      data() {
        return {
          // radio 버튼 데이터바인딩
          checkedItem: '',
          // checkbox 버튼 데이터바인딩
          checkedItems: []
        }
      },
      methods: {
        // radio, checkbox 버튼 이벤트의 실행 함수
        doSelect() {
          if (this.selectType === 'radio') {
            console.log(this.checkedItem)
          } else if (this.selectType === 'checkbox') {
            // console.log(this.checkedItems)
            
            // $emit은 부모 component의 이벤트를 실행 시켜주는 글로벌 함수
            // 파라미터 첫 번째에는 이벤트명, 두 번째에는 데이터 입력
            this.$emit('change-item', this.checkedItems)
          }
        }
      }
    }
    </script>

     


     

    삭제 버튼 추가

    부모 component에서는 자식 component의 checkedbox에서 선택된 값이 무엇인지 알 수 없다. changeCheckedValue 함수는 자식 component의 $emit을 통해 넘겨받은 data를 checkedItems 배열에 담는다. doDelete 함수는 checkedItems 배열에 담긴 값에 해당하는 row를 삭제하게 된다.

     

    부모 component

    삭제 버튼을 생성한다.

    <template>
      <div>
        ...
        <!-- 삭제 버튼 생성 -->
        <button class="btn btn-danger" @click="doDelete">삭제</button>

     

    자식 component로부터 전달받은 data(선택된 값)를 담을 checkedItems 배열을 지정한다.

    data() {
        return {
          ...
          // 자식 component의 data(선택된 값)를 담는 배열
          checkedItems: []

     

    changeCheckedValue 함수가 자식 component의 data를 checkedItems 배열에 넣을 수 있도록 수정한다.

    methods: {
        doSearch() {
        ...
        // 파라미터에 자식 component의 data를 넣는다.
        changeCheckedValue(data) {
          // 자식 component의 data를 checkedItems 배열에 넣는다.
          this.checkedItems = data
    
          // console.log('부모 component : ', data)
        },

     

    삭제 함수 doDelete를 선언한다. (이 코드는 테스트 코드로서 실제 값을 삭제하지 않는다. console.log를 이용해 선택된 값과 삭제할 값이 일치하는지를 확인한다.)

    methods: {
        ...
        // 삭제 함수 선언
        doDelete() {
          console.log(this.checkedItems)
        }

     

    [그림 2] 삭제 버튼 클릭 후 콘솔창

     

    ListView.vue 전체 코드

    더보기
    <template>
      <div>
        <button class="btn btn-danger" @click="doSearch">조회</button>
        <!-- 삭제 버튼 생성 -->
        <button class="btn btn-danger" @click="doDelete">삭제</button>
        <!-- selectType는 radio를 문자열 그대로 넘길 것이기 때문에 데이터바인딩이 불필요하다. -->
        <!-- simple-grid 내에 커스텀 이벤트(@change-item)를 만들 수 있다. -->
        <simple-grid
          :headers="headers"
          :items="drinkList"
          selectType="checkbox"
          ckeckedKey="drinkId"
          @change-item="changeCheckedValue"
        />
      </div>
    </template>
    <script>
    // @를 사용하면 vue파일 경로가 변경되더라도 에러가 발생하지 않는다.
    import SimpleGrid from '@/components/fragments/SimpleGrid.vue'
    export default {
      components: { SimpleGrid },
      data() {
        return {
          // SimpleGrid.vue에 thead 정보를 전달
          headers: [
            { title: '제품번호', key: 'drinkId' },
            { title: '제품명', key: 'drinkName' },
            { title: '가격', key: 'price' }
          ],
          drinkList: [],
          // 자식 component의 data(선택된 값)를 담는 배열
          checkedItems: []
        }
      },
      methods: {
        doSearch() {
          // SimpleGrid.vue에 tbody 정보를 전달
          this.drinkList = [
            {
              drinkId: '1',
              drinkName: '코카콜라',
              price: 700,
              qty: 1
            },
            {
              drinkId: '2',
              drinkName: '오렌지주스',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '3',
              drinkName: '커피',
              price: 500,
              qty: 1
            },
            {
              drinkId: '4',
              drinkName: '물',
              price: 700,
              qty: 1
            },
            {
              drinkId: '5',
              drinkName: '보리차',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '6',
              drinkName: '포카리',
              price: 1000,
              qty: 1
            },
            {
              drinkId: '7',
              drinkName: '뽀로로',
              price: 1300,
              qty: 1
            }
          ]
        },
        // 파라미터에 자식 component의 data를 넣는다.
        changeCheckedValue(data) {
          // 자식 component의 data를 checkedItems 배열에 넣는다.
          this.checkedItems = data
    
          // console.log('부모 component : ', data)
        },
        // 삭제 함수 선언
        doDelete() {
          console.log(this.checkedItems)
        }
      }
    }
    </script>

     


     

    custom event 관련 하드코딩 방지

    지금까지의 코드에서는 부모 component의 custom event 명에 변경이 있을 경우 자식 component의 custom event 명도 일일이 변경해 주어야 한다. 이러한 하드코딩을 방지하기 위해 다음과 같이 코드를 수정할 수 있다.

     

    부모 component의 simple-grid에 changeEventName를 추가하고 "change-item2"로

    <simple-grid
      ...
      changeEventName="change-item2"
      @change-item2="changeCheckedValue"

     

    자식 component props에 changeEventName 프로퍼티를 만든다. 이렇게 함으로써 자식 component에서의 custom event 명을 항상 changeEventName이 된다.

    props: {
        ...
        // 커스텀 이벤트의 기본값이 change-item인 changeEventName 프로퍼티를 만든다.
        changeEventName: {
          type: String,
          default: 'change-item'
        }

     

    doSelect 함수에서 $emit의 첫 번째 파라미터의 값을 props의 changeEventName으로 수정한다.

    methods: {
        doSelect() {
          ...
            // 첫 번째 파라미터의 명을 props의 changeEventName으로 수정한다.
            this.$emit(this.changeEventName, this.checkedItems)
          }
        }
      }

     

    ListView.vue 전체 코드

    더보기
    <template>
      <table class="table table-bordered">
        <thead>
          <tr>
            <!-- radio, checkbox 컬럼의 헤드를 만든다. -->
            <th v-if="selectType === 'radio' || selectType === 'checkbox'"></th>
            <th :key="th.key" v-for="th in headers">{{ th.title }}</th>
          </tr>
        </thead>
        <tbody>
          <tr :key="i" v-for="(item, i) in items">
            <!-- radio 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'radio'">
              <!-- radio 버튼을 checkedItem으로 그룹핑한다. -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <input
                type="radio"
                name=""
                id=""
                v-model="checkedItem"
                @change="doSelect"
              />
            </td>
            <!-- checkbox 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'checkbox'">
              <!-- checkbox 버튼을 checkedItems으로 그룹핑한다.  -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <!-- value로 유일한 Key값인 drinkId를 데이터바인딩, 어떤 row가 선택되었는지 알 수 있다. -->
              <input
                type="checkbox"
                name=""
                id=""
                :value="item[ckeckedKey]"
                v-model="checkedItems"
                @change="doSelect"
              />
            </td>
            <td :key="th.key" v-for="th in headers">{{ item[th.key] }}</td>
          </tr>
        </tbody>
      </table>
    </template>
    <script>
    export default {
      components: {},
      // props에서 부모 component로 부터 데이터를 받는다.
      props: {
        headers: {
          type: Array,
          default: function () {
            return []
          }
        },
        items: {
          type: Array,
          default: function () {
            return []
          }
        },
        // 부모 component로 부터 radio, checkbox 문자열을 받는다.
        selectType: {
          type: String,
          default: ''
        },
        // 부모 component로 부터 drinkId를 받는다.
        ckeckedKey: {
          type: String,
          default: ''
        },
        // 커스텀 이벤트의 기본값이 change-item인 changeEventName 프로퍼티를 만든다.
        changeEventName: {
          type: String,
          default: 'change-item'
        }
      },
      data() {
        return {
          // radio 버튼 데이터바인딩
          checkedItem: '',
          // checkbox 버튼 데이터바인딩
          checkedItems: []
        }
      },
      methods: {
        // radio, checkbox 버튼 이벤트의 실행 함수
        doSelect() {
          if (this.selectType === 'radio') {
            console.log(this.checkedItem)
          } else if (this.selectType === 'checkbox') {
            // console.log(this.checkedItems)
    
            // $emit은 부모 component의 이벤트를 실행 시켜주는 글로벌 함수
            // 파라미터 첫 번째에는 이벤트명, 두 번째에는 데이터 입력
            // 첫 번째 파라미터의 명을 props의 changeEventName으로 변경한다.
            this.$emit(this.changeEventName, this.checkedItems)
          }
        }
      }
    }
    </script>

     

    SimpleGrid.vue 전체 코드

    더보기
    <template>
      <table class="table table-bordered">
        <thead>
          <tr>
            <!-- radio, checkbox 컬럼의 헤드를 만든다. -->
            <th v-if="selectType === 'radio' || selectType === 'checkbox'"></th>
            <th :key="th.key" v-for="th in headers">{{ th.title }}</th>
          </tr>
        </thead>
        <tbody>
          <tr :key="i" v-for="(item, i) in items">
            <!-- radio 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'radio'">
              <!-- radio 버튼을 checkedItem으로 그룹핑한다. -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <input
                type="radio"
                name=""
                id=""
                v-model="checkedItem"
                @change="doSelect"
              />
            </td>
            <!-- checkbox 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'checkbox'">
              <!-- checkbox 버튼을 checkedItems으로 그룹핑한다.  -->
              <!-- change 이벤트로 checked 확인, doSelect 실행 -->
              <!-- value로 유일한 Key값인 drinkId를 데이터바인딩, 어떤 row가 선택되었는지 알 수 있다. -->
              <input
                type="checkbox"
                name=""
                id=""
                :value="item[ckeckedKey]"
                v-model="checkedItems"
                @change="doSelect"
              />
            </td>
            <td :key="th.key" v-for="th in headers">{{ item[th.key] }}</td>
          </tr>
        </tbody>
      </table>
    </template>
    <script>
    export default {
      components: {},
      // props에서 부모 component로 부터 데이터를 받는다.
      props: {
        headers: {
          type: Array,
          default: function () {
            return []
          }
        },
        items: {
          type: Array,
          default: function () {
            return []
          }
        },
        // 부모 component로 부터 radio, checkbox 문자열을 받는다.
        selectType: {
          type: String,
          default: ''
        },
        // 부모 component로 부터 drinkId를 받는다.
        ckeckedKey: {
          type: String,
          default: ''
        },
        // 커스텀 이벤트의 기본값이 change-item인 changeEventName 프로퍼티를 만든다.
        changeEventName: {
          type: String,
          default: 'change-item'
        }
      },
      data() {
        return {
          // radio 버튼 데이터바인딩
          checkedItem: '',
          // checkbox 버튼 데이터바인딩
          checkedItems: []
        }
      },
      methods: {
        // radio, checkbox 버튼 이벤트의 실행 함수
        doSelect() {
          if (this.selectType === 'radio') {
            console.log(this.checkedItem)
          } else if (this.selectType === 'checkbox') {
            // console.log(this.checkedItems)
    
            // $emit은 부모 component의 이벤트를 실행 시켜주는 글로벌 함수
            // 파라미터 첫 번째에는 이벤트명, 두 번째에는 데이터 입력
            // 첫 번째 파라미터의 명을 props의 changeEventName으로 변경한다.
            this.$emit(this.changeEventName, this.checkedItems)
          }
        }
      }
    }
    </script>

     


     

    부모 component에서 자식 component의 data, methods 접근

    ref를 이용해 부모 component에서 자식 component의 data와 methods에 접근할 수 있다. 

     

    자식 component

    먼저 자식 component인 SimpleGrid.vue에서 data에는 sampleData를 추가한다. 

    data() {
        return {
          ...
          // 부모 componet에서 접근하고자 하는 data인 sampleData
          sampleData: 'A'

     

    method에는 doPrint 함수를 선언한다.

    methods: {
        ...
        // 부모 component에서 호출하고자 하는 doPrint 함수를 선언
        doPrint() {
          console.log('doPrint 함수 호출')
          console.log(this.sampleData)
        }

     

    SimpleGrid.vue 전체 코드

    더보기
    <template>
      <table class="table table-bordered">
        <thead>
          <tr>
            <!-- radio, checkbox 컬럼의 헤드를 만든다. -->
            <th v-if="selectType === 'radio' || selectType === 'checkbox'"></th>
            <th :key="th.key" v-for="th in headers">{{ th.title }}</th>
          </tr>
        </thead>
        <tbody>
          <tr :key="i" v-for="(item, i) in items">
            <!-- radio 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'radio'">
              <!-- radio 버튼을 checkedItem으로 그룹핑한다. -->
              <!-- change 이벤트로 checked 확인, doSelect 함수 호출 -->
              <input
                type="radio"
                name=""
                id=""
                v-model="checkedItem"
                @change="doSelect"
              />
            </td>
            <!-- checkbox 컬럼의 바디를 만든다. -->
            <td v-if="selectType === 'checkbox'">
              <!-- checkbox 버튼을 checkedItems으로 그룹핑한다.  -->
              <!-- change 이벤트로 checked 확인, doSelect 함수 호출 -->
              <!-- value로 유일한 Key값인 drinkId를 데이터바인딩, 어떤 row가 선택되었는지 알 수 있다. -->
              <input
                type="checkbox"
                name=""
                id=""
                :value="item[ckeckedKey]"
                v-model="checkedItems"
                @change="doSelect"
              />
            </td>
            <td :key="th.key" v-for="th in headers">{{ item[th.key] }}</td>
          </tr>
        </tbody>
      </table>
    </template>
    <script>
    export default {
      components: {},
      // props에서 부모 component로 부터 데이터를 받는다.
      props: {
        headers: {
          type: Array,
          default: function () {
            return []
          }
        },
        items: {
          type: Array,
          default: function () {
            return []
          }
        },
        // 부모 component로 부터 radio, checkbox 문자열을 받는다.
        selectType: {
          type: String,
          default: ''
        },
        // 부모 component로 부터 drinkId를 받는다.
        ckeckedKey: {
          type: String,
          default: ''
        },
        // 커스텀 이벤트의 기본값이 change-item인 changeEventName 프로퍼티를 만든다.
        changeEventName: {
          type: String,
          default: 'change-item'
        }
      },
      data() {
        return {
          // radio 버튼 데이터바인딩
          checkedItem: '',
          // checkbox 버튼 데이터바인딩
          checkedItems: [],
          // 부모 componet에서 접근하고자 하는 data인 sampleData
          sampleData: 'A'
        }
      },
      methods: {
        // radio, checkbox 버튼 이벤트의 실행 함수
        doSelect() {
          if (this.selectType === 'radio') {
            console.log(this.checkedItem)
          } else if (this.selectType === 'checkbox') {
            // console.log(this.checkedItems)
    
            // $emit은 부모 component의 이벤트를 실행 시켜주는 글로벌 함수
            // 파라미터 첫 번째에는 이벤트명, 두 번째에는 데이터 입력
            // 첫 번째 파라미터의 명을 props의 changeEventName으로 변경한다.
            this.$emit(this.changeEventName, this.checkedItems)
          }
        },
        // 부모 component에서 호출하고자 하는 doPrint 함수를 선언
        doPrint() {
          console.log('doPrint 함수 호출')
          console.log(this.sampleData)
        }
      }
    }
    </script>

     

    부모 component

    부모 component인 ListView.vue의 simple-grid에 ref를 추가한다. ref는 유일한 값을 가진다.

    <simple-grid
          ...
          ref="smGrid"
        />

     

    methods의 doDelete 함수에 $refs를 이용해 자식 component의 data(sampleData)와 method(doPrint)를 호출하게 한다.

    methods: {
        ...
        // 삭제 함수 선언
        doDelete() {
          ...
          // $refs는 전체 ref를 의미한다.
          this.$refs.smGrid.sampleData = 'B'
          this.$refs.smGrid.doPrint()
        }
      }

     

    ListView.vue 전체 코드

    더보기
    <template>
      <div>
        <button class="btn btn-danger" @click="doSearch">조회</button>
        <!-- 삭제 버튼 생성 -->
        <button class="btn btn-danger" @click="doDelete">삭제</button>
        <!-- selectType는 radio를 문자열 그대로 넘길 것이기 때문에 데이터바인딩이 불필요하다. -->
        <!-- simple-grid 내에 커스텀 이벤트(@change-item)를 만들 수 있다. -->
        <!-- 자식 component에서 커스텀 이벤트의 이름을 변경하는 하드코딩을 방지하기 위해 changeEventName을 추가한다. -->
        <!-- ref는 유일한 값을 가진다. 여기에서는 부모 component에서 자식 component의 doPrint 함수를 호출하기 위해서 사용한다. -->
        <simple-grid
          :headers="headers"
          :items="drinkList"
          selectType="checkbox"
          ckeckedKey="drinkId"
          changeEventName="change-item2"
          @change-item2="changeCheckedValue"
          ref="smGrid"
        />
      </div>
    </template>
    <script>
    // @를 사용하면 vue파일 경로가 변경되더라도 에러가 발생하지 않는다.
    import SimpleGrid from '@/components/fragments/SimpleGrid.vue'
    export default {
      components: { SimpleGrid },
      data() {
        return {
          // SimpleGrid.vue에 thead 정보를 전달
          headers: [
            { title: '제품번호', key: 'drinkId' },
            { title: '제품명', key: 'drinkName' },
            { title: '가격', key: 'price' }
          ],
          drinkList: [],
          // 자식 component의 data(선택된 값)를 담는 배열
          checkedItems: []
        }
      },
      methods: {
        doSearch() {
          // SimpleGrid.vue에 tbody 정보를 전달
          this.drinkList = [
            {
              drinkId: '1',
              drinkName: '코카콜라',
              price: 700,
              qty: 1
            },
            {
              drinkId: '2',
              drinkName: '오렌지주스',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '3',
              drinkName: '커피',
              price: 500,
              qty: 1
            },
            {
              drinkId: '4',
              drinkName: '물',
              price: 700,
              qty: 1
            },
            {
              drinkId: '5',
              drinkName: '보리차',
              price: 1200,
              qty: 1
            },
            {
              drinkId: '6',
              drinkName: '포카리',
              price: 1000,
              qty: 1
            },
            {
              drinkId: '7',
              drinkName: '뽀로로',
              price: 1300,
              qty: 1
            }
          ]
        },
        // 파라미터에 자식 component의 data를 넣는다.
        changeCheckedValue(data) {
          // 자식 component의 data를 checkedItems 배열에 넣는다.
          this.checkedItems = data
    
          // console.log('부모 component : ', data)
        },
        // 삭제 함수 선언
        doDelete() {
          console.log(this.checkedItems)
          // $refs는 전체 ref를 의미한다.
          this.$refs.smGrid.sampleData = 'B'
          this.$refs.smGrid.doPrint()
        }
      }
    }
    </script>

     

    삭제 버튼(부모 component)을 클릭하면 [그림 3]과 같이 콘솔 창에 자식 component의 doPrint 함수가 호출되고 sampleData의 값이 A에서 B로 변경된 것을 확인할 수 있다.

    [그림 3] 삭제 버튼 클릭시 콘솔 창 결과

     


     

    boolean에서 (v-bind):

    enableExcelDownload="true"에 colon을 붙이면 boolean 값을 전달하고, 붙이지 않으면 문자열로 전달한다. boolean은 값이 존재하면 true로 인식하기 때문에 아래 코드의 경우에는 문자열로 가더라도 true로 인식하겠지만, 만약 "false"를 colon 없이 전달하면 false가 아닌 문자열 "false"로 인식하여 true가 전달되므로 주의!

    <!-- src\views\4_reuse\ListView.vue -->
    <template>
      <div>
        ...
        <simple-grid
          ...
          :enableExcelDownload="true"
          ...

     

    728x90
    반응형
    댓글