티스토리 뷰
이번에는 v-model 키워드를 활용해서 form 요소에 양방향 바인딩(2 way binding)하는 방법을 배워 봅니다. 또한 사용자 입력값을 유효성 체크하는 로직도 추가합니다.
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body>
<div class="nav-bar"></div>
<div id="app">
<div class="cart">
<p>Cart({{ cart.length }})</p>
</div>
<product :premium="premium" @add-to-cart="addToCart"></product>
<div>
<h2>Reviews</h2>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
<product-review @review-submitted="addReview"></product-review>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="main.js"></script>
</body>
</html>
input, textarea, select, submit 요소를 가진 리뷰(product-review) 컴포넌트를 추가하고 v-model로 각 값을 바인딩시킵니다.
리액트의 경우 state를 컴포넌트 내부에 선언하고 setState 함수로 업데이트하는 방식인데 vue 경우 v-model로 연동하면 자동으로 바인딩돼서 더 간단한 느낌입니다.
Vue.component("product", {
props: {
premium: {
type: Boolean,
required: true
},
},
template: `
<div class="product">
<div class="product-image">
<img v-bind:src="image"/>
</div>
<div class="product-info">
<h1>{{ title }}</h1><span v-show="onSale">On Sale!</span>
<p v-if="inStock">In Stock</p>
<p v-else>Out of Stock</p>
<p>Shipping: {{ shipping }}</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div class="color-box"
v-for="(variant, index) in variants"
:key="variant.variantId"
:style="{backgroundColor: variant.variantColor}"
@mouseover="updateProduct(index)"
>
</div>
<button v-on:click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>Add to cart</button>
</div>
</div>
`,
data(){
return {
brand: 'Vue Mastery',
product: 'Socks',
selectedVarint: 0,
onSale: false,
details: ["80% cotton", "20% polyester", "Gender-neutral" ],
variants: [
{
variantId: 2234,
variantColor: "green",
variantImage: "./assets/vmSocks-green-onWhite.jpg",
variantQuantity: 10
},
{
variantId: 2235,
variantColor: "blue",
variantImage: "./assets/vmSocks-blue-onWhite.jpg",
variantQuantity: 0
}
],
cart: 0,
}
},
methods: {
addToCart(){
this.$emit("add-to-cart", this.variants[this.selectedVarint].variantId)
},
updateProduct(index){
this.selectedVarint = index
},
},
computed: {
title(){
return `${this.brand} ${this.product}`
},
image(){
return this.variants[this.selectedVarint].variantImage
},
inStock(){
return this.variants[this.selectedVarint].variantQuantity
},
shipping(){
if(this.premium){
return "Free"
} else {
return 2.99
}
}
}
})
Vue.component("product-review", {
template: `
<form class="review-form" @submit.prevent="onSubmit">
<p v-if="errors.length">
<b>Please correct the following errors(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
<p>
<label for="name">Name:</label>
<input id="name" v-model="name" placeholder="name">
</p>
<p>
<label for="review">Review:</label>
<textarea id="review" v-model="review"></textarea>
</p>
<p>
<label for="rating">Rating:</label>
<select id="rating" v-model.number="rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
`,
data() {
return {
name: null,
review: null,
rating: 5,
errors: [],
}
},
methods: {
onSubmit(){
if(!this.name || !this.review || ! this.rating){
this.errors = []
if(!this.name) this.errors.push("Name required")
if(!this.review) this.errors.push("Review required")
if(!this.rating) this.error.push("Rating required")
return
}
const productReview = {
name: this.name,
review: this.review,
rating: this.rating
}
this.$emit('review-submitted', productReview)
this.name = null
this.review = null
this.rating = 5
}
}
})
var app = new Vue({
el: '#app',
data: {
premium: false,
cart: [],
reviews: []
},
methods: {
addToCart(id){
this.cart.push(id)
},
addReview(productReview){
this.reviews.push(productReview)
}
}
})
또한 이벤트 핸들링 시. prevent 값을 추가하면 이벤트의 기본 동작을 쉽게 막을 수 있습니다.
물론 연결될 onSubmit 함수 인자로 event 객체가 전달되기 때문에 메서드 내에서 event.preventDefault()를 수행시킬 수도 있습니다.
에러 메시지가 지속적으로 노출되는 버그가 있어서 전송 후 에러 데이터를 초기화하는 코드를 넣었습니다.
이름, 리뷰, 점수 중 하나라도 빈 값이면 체크하는 로직을 코드 상단으로 배치했습니다. 개인 취향이지만 예외 조건을 상단에서 처리하면 이후 코드가 수행되지 않아 불필요한 코드 실행을 방지할 수 있습니다.
댓글