컴포넌트, element-plus

keep_going·2022년 12월 23일
0

vue

목록 보기
5/13
  • 참조 ) 컴포넌트란? https://hanamon.kr/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-component%EB%9E%80/

  • 컴포넌트에 들어가는 내용은 사용하는 사람들의 데이터를 기반으로 해야한다. 컴포넌트에서 내용 정해주면 내가 원하는 값 못넣는데 쓸 이유 없음.

  • 따라서 기본 골격은 유지하지만 내용은 바꿀수 있도록!

  • 컴포넌트의 속성값(attributes)으로 기능 구현. 많으면 많을수록 쓸수 있는 환경 다양해지니까 좋아. 속성값에 없는 기능을 구현하고 싶으면... 다른 컴포넌트 써야지 뭐

  • 발생시킬수 있는 event 종류도 정해져 있음


  • 컴포넌트의 구조를 파악하기 위해 직접 컴포넌트를 생성해서 실습했지만 실제로는 다른사람들이 만들어 놓은 컴포넌트를 가져와서 활용한다.
  • components 아래 comp 폴더에 생성된 Menu1Page, Menu2Page, Menu3Page, Menu4Page는 다른 사람들이 만들어 놓은 component 역할, CompPage는 내가 작성한 페이지 역할.

// 파일명 : 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>
  • props와 store는 다른 components 끼리 값을 공유 할수 있게 해준다. 그럼 같은건가?
  • no! store는 다른 components를 거치지 않고 데이터를 전달 가능하지만 props는 연결 되어있는 component에만 전달가능
  • 거쳐야 하는 단계가 많아질수록 복잡해지고 수정할때 골치 아프다. 여러 components에 동시에 적용되는 거라면 store(vuex, reduct 등..)가 더 알맞다!

// 파일명 : 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

  • 다른 사람들이 만들어 놓은 component를 내 작업물에 적용시켜보자!
  • element plus 사이트에서 installation 설명대로 npm.. 쳐서 설치
  • nodemodules 보면 설치 확인 가능
  • 실습때 했던것 처럼 내가 작성한 components에 import 하고 가져와야지 쓸 수 있는데 내가 이미 components를 몇백개 만들었으면 어떻게 일일히 등록하나? -> main에 한번만 등록하면 모든 vue에 적용된다!
  • main.js에서 이미 있는 항목 빼고 없는거만 가져오면 되겠네~
    import ElementPlus from 'element-plus'
    import 'element-plus/dist/index.css'
    app.use(ElementPlus)
  • 사용방법은 element plus 사이트에서 component에서 원하는것 찾아서 <> 누르면 source를 보여주는데 거기에 맞춰서 쓰면 됨

// 파일명 : 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>
profile
keep going

0개의 댓글