참조 ) 컴포넌트란? https://hanamon.kr/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-component%EB%9E%80/
컴포넌트에 들어가는 내용은 사용하는 사람들의 데이터를 기반으로 해야한다. 컴포넌트에서 내용 정해주면 내가 원하는 값 못넣는데 쓸 이유 없음.
따라서 기본 골격은 유지하지만 내용은 바꿀수 있도록!
컴포넌트의 속성값(attributes)으로 기능 구현. 많으면 많을수록 쓸수 있는 환경 다양해지니까 좋아. 속성값에 없는 기능을 구현하고 싶으면... 다른 컴포넌트 써야지 뭐
발생시킬수 있는 event 종류도 정해져 있음
// 파일명 : CompPage.vue
<template>
<div>
<h3>컴포넌트 실습</h3>
<hr />
<menu-1-page title="props메뉴1" label="aaa"></menu-1-page>
// 3. 적절한 위치에 사용한다.
<menu-2-page title="props메뉴2" @myevent="handleNum"></menu-2-page>
// menu2에서 comp의 이벤트 호출->comp에서 함수 발동
// 함수명 뒤에 () 붙이지 마!
<menu-3-page>
<template #output1>
<p>slot로 전송되는 태그 출력</p>
</template>
<template #output2>
<input type="text" value="aaa" />
<button @click="handleOutput2()">전송</button>
</template>
</menu-3-page>
// menu3은 내가 만든(comp에서) 데이터를 출력함
<menu-4-page>
<template #default="scope">
{{ scope }}
// 데이터 잘 오는지 확인하기 위한것
// default는 menu4에서 데이터를 가지고 있는 slot이고 그걸 또 scope라고 불러줌. 굳이? 싶지만 문법인가보다~
<input type="text" :value="scope.data.userid"/>
</template>
</menu-4-page>
// component 내부(menu4)의 데이터를 받아와서 화면을 만든 다음에 다시 넣어줌. input에 받은 데이터를 보여줄 수 있음
// menu3과 menu4은 데이터의 주체가 다르다!
</div>
</template>
<script>
import Menu1Page from './comp/Menu1Page.vue'; // 1. import 먼저 시킨다.
import Menu2Page from './comp/Menu2Page.vue';
import Menu3Page from './comp/Menu3Page.vue';
import Menu4Page from './comp/Menu4Page.vue';
export default {
// component 문법 : 파스칼(Menu1Page), 케밥(menu-1-page)
// script에 등록할때는 파스칼 형태로, template에 쓸때는 케밥형태로 쓰는걸 권장
// 파스칼로 등록하면 케밥형태로 떠야하는데 안떠서 extension에서 vetur깔아서 해결함
components : {
Menu1Page, // 2. 등록한다.
Menu2Page,
Menu3Page,
},
setup () {
const handleNum = (e) =>{
alert(e);
};
// 값을 넣어준 곳은 menu2지만 반응은 comp에서 한 것
const handleOutput2 = () =>{
alert('output2');
};
return {
handleNum,
handleOutput2
}
}
}
</script>
<style lang="css" scoped>
</style>
// 파일명 : Menu1Page.vue
<template>
<div style="border : 1px solid #cccccc;padding:5px">
<h3>{{ state.title }}</h3>
<label>{{ state.label }}</label>
</div>
</template>
<script>
import { reactive } from '@vue/reactivity'
export default {
props : { // 사용하는 컴포넌트(부모 컴포넌트)에서 전달되는 값 설정
title : {
type : String,
default : 'Menu1컴포넌트' // 부모에서 안주면 띄우는 기본값
},
label : {
type : String,
default : ''
}
},
setup ( props ) {
const state = reactive({
title : props.title,
label : props.label,
});
return {
state
};
}
}
</script>
<style lang="css" scoped>
</style>
// 파일명 : Menu2Page.vue
<template>
<div style="border : 1px solid #cccccc; padding:5px">
<h3>{{ state.title }}</h3>
// 부모에게서 title값 받아옴
<input type="text" v-model="state.num" />
<button @click="handleClick()">전송</button>
// 전송을 누르면 여기서는 알수 있지만 부모한테는 어떻게 알리나? -> context
</div>
</template>
<script>
import { reactive } from '@vue/reactivity'
export default {
props : {
title : {
type : String,
default : '메뉴2제목'
}
},
setup (props, context) {
// 원래 이런 형태인데 이때까지 props, context를 안썼으니 생략했던것
// props는 내용 받을때 사용, context는 내용 보낼때 사용
const state = reactive({
title : props.title,
num : 10,
});
const handleClick = () => {
// alert(state.num);
// 부모컴포넌트의 특정 이벤트발생(이벤트명, 전송값)
context.emit('myevent', state.num);
};
return {
state,
handleClick
};
}
}
</script>
<style lang="css" scoped>
</style>
// 파일명 : Menu3Page.vue
<template>
<div style="border : 1px solid #cccccc;padding:5px">
<h3>컴포넌트메뉴3</h3>
<slot name="output1"></slot>
// 출력할 위치
<hr />
<slot name="output2"></slot>
// slot 이름을 ouput1,2로 준것
</div>
</template>
// 이때 slot은 출력할 위치만 지정해줌. 화면 자체를 보낼수 있다.
// 확장성이 제일 좋지만.. 이럴꺼면 컴포넌트 쓰는 이유가 없다!
// 어쩔수 없이 컴포넌트 기능이 커버를 못하는 경우에만 사용
<script>
export default {
setup () {
return {}
}
}
</script>
<style lang="css" scoped>
</style>
// 파일명 : Menu4Page.vue
<template>
<div style="border : 1px solid #cccccc; padding:5px">
<h3>컴포넌트메뉴4</h3>
<slot name="default" :data="state"></slot>
// :data를 쓰면 데이터를 부모쪽으로 준다
// slot 이름을 default로 지어준것
</div>
</template>
<script>
import { reactive } from '@vue/reactivity'
export default {
setup () {
const state = reactive({
userid : 'aaa',
username : 'bbb'
})
return {
state
}
}
}
</script>
<style lang="css" scoped>
</style>
=======================================
CMD> npm i element-plus --save
// 파일명 : BoardPage.vue
button, input, table, pagination 에 component 적용
<template>
<div class="container">
<h3>게시판</h3>
<router-link to='/boardinsert'>
<button>글쓰기1</button>
</router-link>
<router-link :to="{path:'/boardinsert', query:{no:1}}">
<button>글쓰기2</button>
</router-link>
<el-input type="text" size="small" v-model="board.text" @keyup.enter="handleText()" placeholder="검색어 입력" style="width:200px;"/>
// : 없으면 String
<el-table :data="board.rows" style="width:100%; cursor:pointer;" @row-click="handleContent1">
<el-table-column prop="_id" label="글번호" width="80px;"/>
<el-table-column prop="title" label="제목" />
<el-table-column prop="writer" label="작성자" />
<el-table-column prop="hit" label="조회수" />
<el-table-column prop="regdate" label="날짜" width="160px;"/>
</el-table>
<el-pagination small layout="prev, pager, next" @current-change="handlePage" :total="board.total">
</el-pagination>
</div>
</template>
<script>
import { reactive, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
setup () {
const router = useRouter();
// 초기값설정-> 숫자: 0, 문자: '', 배열: null
const board = reactive({
total : 0, // 전체 게시물 수
rows : null, // 게시물 내용
page : 1, // 페이지 정보
text : '', // 검색어 정보
});
const handleText = () => {
board.page = 1; // 검색 완료됐을땐 1페이지에서 띄움
handleData();
};
const handlePage = (page) => {
console.log(page);
board.page = page; // 상태변수값 변경
handleData(); // 게시물 읽기
};
// parameter 3개 라서 일단 a,b,c로 지정해서 콘솔 찍어봄
// 하나만 있어도 id알수있으므로 하나만 받아와도 될꺼 같다.
const handleContent1 = (row) => {
console.log(row._id);
handleContent (row._id);
// 복붙 귀찮으니까 호출한것
};
const handleContent = async( tmp ) => {
console.log('handleContent');
const url =`/board101/updatehit.json?no=${tmp}`;
const headers = {"Content-Type":"application/json"};
const { data } = await axios.put(url, {}, {headers});
console.log(data);
if(data.status === 200) {
router.push({path:'/boardcontent', query:{no:tmp}});
}
};
const handleData = async() => {
const url = `/board101/select.json?page=${board.page}&text=${board.text}`;
const headers = {"Content-Type":"application/json"};
const { data } = await axios.get(url, {headers});
console.log(data);
if(data.status === 200) {
board.rows = data.rows;
board.total = data.total
}
};
onMounted(() =>{
handleData();
});
return {
board,
handleContent,
handleContent1,
handlePage,
handleText
};
}
}
</script>
<style lang="css" scoped>
.container {
width: 600px;
background-color: rgb(255, 251, 255);
margin: 0px auto;
padding: 20px;
}
table {
width: 100%;
border-collapse: collapse;
}
</style>
// 파일명 : ItemPage.vue
<template>
<div>
<h3>물품목록</h3>
<router-link :to ="{ path :'/iteminsert' }">
<button>물품등록</button>
</router-link>
<el-table :data="state.rows" style="width: 100%; cursor: pointer;" @row-click="handleContent1">
<el-table-column prop="_id" label="물품번호" width="100" />
<el-table-column prop="name" label="물품명" width="180" />
<el-table-column prop="content" label="물품내용" width="250" />
<el-table-column prop="price" label="물품가격" width="180" />
<el-table-column prop="quantity" label="물품수량" width="180" />
<el-table-column prop="regdate" label="등록일" width="180" />
</el-table>
</div>
</template>
<script>
import axios from 'axios';
import { reactive, onMounted } from 'vue';
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
const state = reactive({
rows: null,
});
const handleContent1 = (a) => {
console.log(a._id);
handleContent(a._id);
};
const handleData = async () => {
const url = `/item101/selectlist.json`;
const headers = { "Content-Type": "application/json" };
const { data } = await axios.get(url, { headers });
console.log(data);
if (data.status === 200) {
state.rows = data.result;
}
};
const handleContent = ( code ) => {
console.log('handleContent', code);
router.push({path:'/itemcontent', query:{code:code}});
};
onMounted(() => {
handleData();
});
return {
state,
handleContent,
handleContent1
};
}
}
</script>
<style lang="css" scoped>
</style>