사용자 등록을 하는 컴포넌트를 하나 만든 후 수정을 할 때 어떻게 하면 만든 컴포넌트를 재사용할 수 있을까 고민하다 만들어본 소스입니다.
디자인 프레임워크인 VuetifyJS를 사용해서 만들었습니다.
먼저 구성 화면
위 화면은 사용자 리스트를 그려주는 listTable.vue파일이고 사용자 등록 버튼을 누르거나 리스트에 있는 펜 모양 아이콘을 누르면
사용자 정보 컴포넌트인 editUser.vue 파일이 열린다.
listTable.vue 파일 소스
<template>
<v-card>
<v-card-title>
<v-spacer></v-spacer>
<edit-user :user_info="user_info_data" @set_info="set_user_info"/>
</v-card-title>
<v-card-title>
<v-spacer></v-spacer>
<v-spacer></v-spacer>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
></v-text-field>
</v-card-title>
<v-card-text>
<v-data-table
:headers="headers"
:items="listData"
:page.sync="page"
:items-per-page="itemsPerPage"
hide-default-footer
class="elevation-1"
@page-count="pageCount = $event"
:search="search"
height="500"
:loading="loading"
loading-text="데이터를 불러오는 중입니다."
no-data-text="데이터가 없습니다."
>
<template v-slot:[`item.actions`]="{ item }">
<v-icon
small
class="mr-2"
@click="editItem(item)"
>
mdi-pencil
</v-icon>
<v-icon
small
@click="deleteItem(item)"
>
mdi-delete
</v-icon>
</template>
</v-data-table>
<div class="text-center pt-2">
<v-pagination v-model="page" :length="pageCount"></v-pagination>
</div>
</v-card-text>
</v-card>
</template>
<script>
import editUser from '~/components/setting/user/editUser'
export default {
props:["list"],
data() {
return {
user_info: null,
search:'',
page: 1,
pageCount: 0,
itemsPerPage: 10,
headers: [ //props로 받아서 처리하게끔 변경해보기
{ text: '아이디', value: 'user_id' },
{ text: '사용자명', value: 'user_nm' },
{ text: '권한', value: 'user_auth_nm' },
{ text: '설명', value: 'user_desc' },
{ text: '-', value: 'actions', sortalbe: false}
]
}
},
computed: {
listData() { //리스트 불러오기
return this.list;
},
user_info_data() { //상세정보
return this.user_info;
},
loading() {
return this.$store.getters['GET_LOADING_1'];
}
},
methods: {
editItem(data) {
this.user_info = data;
},
async deleteItem(data) {
if (confirm(`${data.user_id} 계정을 삭제하시겠습니까?`)) {
const rs = await this.$store.dispatch('setting/user/deleteUser', data._id);
if (rs.data.result.error == false) {
this.$store.dispatch('setting/user/initUserList');
} else {
alert(rs.data.msg)
}
}
},
set_user_info(data) {
this.user_info = data;
}
},
components: { editUser }
}
</script>
<style>
</style>
봐 둘 부분은 수정 버튼을 누르면 실행하는 editItem(data) 부분과 editUser.vue 컴포넌트에 넘길 속성으로 만든 user_info 부분,
editUser.vue 컴포넌트로부터 데이터를 전달 받을 @set_info="set_user_info" 이렇게 2개다.
수정 버튼을 누르면 user_info값이 null이 었다가 사용자 정보를 넣게 되고,
computed에서 user_info 값이 변경된 값을 user_info_data가 가지게 되는데
editUser.vue 컴포넌트의 속성인 user_info에 그 값을 넘겨준다.
@set_info="set_user_info" 이걸 사용하는 이유는 watch로 데이터 변화를 감지해서 다이얼로그를 열게 해놨더니
한번 수정 눌렀던 데이터를 닫고 다시 똑같은 데이터를 누르면 watch로 감시하고있는 user_info 값이 변화가 없어서 다이얼로그가 열리지 않는 현상이 있어서 해결 방안으로 추가했다.
그리고 editUser.vue 컴포넌트 소스
<template>
<v-row align="start" justify="end">
<v-dialog v-model="dialog" persistent max-width="600px">
<template v-slot:activator="{ on, attrs }">
<v-btn
raised
color="primary"
v-bind="attrs"
v-on="on"
>
사용자 등록
</v-btn>
</template>
<v-card raised outlined class="pa-3" :loading="loading">
<v-card-title>
<span class="headline">사용자 등록</span>
</v-card-title>
<v-card-text>
<v-form ref="form" lazy-validation>
<v-row>
<v-col cols="12">
<v-text-field v-model="user_id" label="아이디*" :rules="user_id_rule" :disabled="state == 'ins' ? false : true" required></v-text-field>
<!-- <v-text-field :value="user_id" @change="v => user_id = v" label="아이디*" :rules="user_id_rule" :disabled="state == 'ins' ? false : true" required></v-text-field> -->
</v-col>
<v-col cols="12">
<v-text-field v-model="user_nm" label="이름*" :rules="user_nm_rule" required></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field v-model="user_pw" label="비밀번호*" type="password" :rules="user_pw_rule"></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field v-model="user_pw_chk" label="비밀번호 확인*" type="password" :rules="user_pw_rule2"></v-text-field>
</v-col>
<v-col cols="12">
<v-select
v-model="user_auth"
label="권한*"
:items="authList"
item-text="name"
item-value="value"
return-object
:rules="user_auth_rule"
>
</v-select>
</v-col>
<v-col cols="12">
<v-text-field v-model="user_desc" label="설명" :rules="user_desc_rule"></v-text-field>
</v-col>
</v-row>
</v-form>
<small class="red--text">*표시는 반드시 입력해야하는 항목입니다.</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn class="ma-2" raised depressed color="primary" @click="save">
<v-icon left>mdi-check</v-icon> 저장
</v-btn>
<v-btn class="ma-2" raised depressed @click="close">
<v-icon left>mdi-close</v-icon> 닫기
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
<script>
export default {
props: [ 'user_info' ],
data() {
return {
dialog: false,
state: 'ins',
authList: [
{ name: '관리자', value: 'A'},
{ name: '일반 사용자', value: 'M'}
],
user_id: '',
user_id_rule: [
v => !!v || '아이디는 필수 입력사항입니다.',
v => /^[a-zA-Z0-9]*$/.test(v) || '아이디는 영문+숫자만 입력 가능합니다.',
v => !( v && v.length >= 15) || '아이디는 15자 이상 입력할 수 없습니다.',
v => this.state === 'ins' ? this.checkDuplicate(v) : true
],
user_nm: '',
user_nm_rule: [
v => !!v || '이름은 필수 입력사항입니다.',
v => !(v && v.length >= 30) || '이름은 30자 이상 입력할 수 없습니다.',
v => !/[~!@#$%^&*()_+|<>?:{}]/.test(v) || '이름에는 특수문자를 사용할 수 없습니다.'
],
user_pw: '',
user_pw_chk: '',
user_pw_rule: [
v => this.state === 'ins' ? !!v || '패스워드는 필수 입력사항입니다.' : true,
v => !(v && v.length >= 30) || '패스워드는 30자 이상 입력할 수 없습니다.',
],
user_pw_rule2: [
v => this.state === 'ins' ? !!v || '패스워드는 필수 입력사항입니다.' : true,
v => !(v && v.length >= 30) || '패스워드는 30자 이상 입력할 수 없습니다.',
v => v === this.user_pw || '패스워드가 일치하지 않습니다.'
],
user_auth: '',
user_auth_rule: [
v => !!v || '권한은 필수 선택 사항입니다.'
],
user_desc: '',
user_desc_rule: [
v => !(v && v.length >= 100) || '설명은 100자 이상 입력할 수 업습니다.'
]
}
},
watch: {
user_info() {
if(this.user_info !== null) {
//listTable 컴포넌트에서 user_info 데이터를 넘기면 수정화면으로 판단 시키고 text field에 데이터를 넣어줌
const user = this.user_info;
this.state = 'upd';
this.user_id = user.user_id;
this.user_nm = user.user_nm;
this.user_auth = user.user_auth_code;
this.user_desc = user.user_desc;
this.dialog = true;
}
}
},
computed: {
loading() {
return this.$store.getters['GET_LOADING_2'];
}
},
methods: {
async save() {
const validate = this.$refs.form.validate();
if (validate) {
if (confirm ('저장하시겠습니까?')) {
const params = {
user_id: this.user_id,
user_nm: this.user_nm,
user_pw: this.user_pw,
user_auth_code: this.user_auth.value,
user_auth_nm: this.user_auth.name,
user_desc: this.user_desc
}
if (this.state == 'upd') {
params._id = this.user_info._id;
params.user_mk_dt = this.user_info.user_mk_dt;
}
try {
this.$nuxt.$loading.start();
const url = (this.state == 'ins' ? 'setting/user/insertUser' : 'setting/user/updateUser');
const rs = await this.$store.dispatch(url, params);
if (rs.data.result.error == false) {
this.$nuxt.$loading.finish();
this.$store.dispatch('setting/user/initUserList');
this.close();
}
} catch (err) {
alert(err);
}
}
}
},
//id 중복체크
checkDuplicate(user_id) {
const user_data = this.$store.getters['setting/user/GET_USER_LIST'];
for(var i in user_data) {
var user_idcheck = user_data[i].user_id;
if(user_id == user_idcheck){
return '이미 사용중인 아이디입니다.';
}
}
return true
},
close() {
this.dialog = false;
this.state = 'ins';
this.$refs.form.reset();
this.$emit('set_info', null);
}
}
}
</script>
<style>
</style>
close 부분에 $emit('set_info', null); 이걸 통해 null을 listTable.vue로 전달을 하고 watch는 user_info가 null 아닐 때만 다이얼로그를 열기 때문에 반응하지 않는다.
사용자 등록 버튼을 누르면 dialog 값이 true가 되면서 등록 화면이 출력된다.
그리고 아까 listTable.vue에서 수정 버튼을 누르면 컴포넌트로 데이터를 넘긴다고 했는데.
이 컴포넌트가 받는 속성으로 props에 user_info 여기로 받게 해 놨다.
watch를 이용해서 user_info를 감시하게 해 놔서 수정 버튼을 눌러서 데이터를 받으면
data() 안에 사용자 데이터에 받은 데이터를 넣어주고 dialog를 true로 바꿔서 컴포넌트를 띄운다.
이런 식으로 컴포넌트를 재활용해봤고, 더 좋은 방법이 있다면 조언 부탁드립니다.
2021-05-28 추가
listTable.vue에서 editUser.vue 컴포넌트 속성으로 @set_info="set_user_info" 라는걸 추가했는데
이게 동일한 데이터를 수정 누르고 닫기 누르고 다시 수정을 누르면 :user_info="user_info_data"로 넘기는 데이터가 똑같기 때문에 editUser.vue에서 watch가 감시중인 user_info의 데이터가 변하지 않아 다이얼로그가 열리지 않는 현상이 발견 됐다.
방법을 찾아보다 $emit을 이용하여 처리하긴 했는데
재사용하기엔 상당히 불필요한 과정이 아닌가 하는 생각이 든다.
나중에 좀더 다른 방법을 찾아봐야할 것 같다.
'VueJS' 카테고리의 다른 글
[VueJS] Vuetify v-btn 에 외부 링크 넣기 (0) | 2021.05.21 |
---|---|
[VueJS] ERROR [vuex] expects string as the type, but found object. (0) | 2021.05.20 |
[VueJS] Vuetify v-text-field 유효성 검사 (rules) (9) | 2021.04.20 |
[VueJS] Vuetify v-select에서 text, value값 가져오기 (0) | 2021.04.18 |
[VueJS] vue-cli + vuex + jwt 로그인 기능 구현하기 (2) | 2021.02.27 |