프론트엔드에서 폼을 처리할 때, 폼 입력 엘리먼트의 상태를 JavaScript의 상태와 동기화해야 하는 경우가 많다. 값 바인딩을 수동으로 연결하고 이벤트 리스너를 변경하는 것은 번거로운 작업이다.
<!--
값이 변경될 때마다 event 객체를 받아와서 text 데이터에 할당
즉 사용자가 폼 입력을 수정할 때마다 JavaScript 상태가 자동으로 업데이트되어 동기화됨
-->
<input :value="text" @input="event => text = event.target.value">
v-model
디렉티브는 위의 내용을 다음과 같이 단순화하는 데 도움이 된다.
<input v-model="text">
또한 v-model은 다른 유형의 입력인 <'textarea> 및 <'select> 엘리먼트에 사용할 수 있다. 사용되는 엘리먼트에 따라 자동으로 다른 DOM 속성 및 이벤트 쌍으로 확장된다.
- 텍스트 유형의 <'input>과 <'textarea> 경우, value 속성과 input 이벤트를 사용한다.
- <'input type="checkbox">과 <'input type="radio"> 경우, checked 속성과 change 이벤트를 사용한다.
- <'select>는 value를 속성으로 사용하고 change를 이벤트로 사용한다.
참고!
v-model은 모든 폼 엘리먼트에서 감지되는 초기 value, checked, selected 속성 값을 무시한다. 항상 현재 바인딩된 JavaScript 상태를 유효한 값으로 취급한다. reactivity API를 사용하여 Javascript에서 초기 값을 선언해야 한다.
1. Basic Usage
1-1. Text
<script>
export default{
data() {
return {
message: ''
}
}
}
</script>
<template>
<p>메세지: {{ message }}</p>
<input v-model="message" placeholder="입력해보라옹"/>
</template>
실행화면
1-2. Multiline text
<script>
export default{
data() {
return {
message: ''
}
}
}
</script>
<template>
<span>여러 줄 메세지: </span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="여러 줄을 추가해 보세요"></textarea>
</template>
실행화면
<'textarea> 내부에 이중 중괄호 문법은 작동하지 않으므로 v-model을 사용해야 한다.
<template>
<span>여러 줄 메세지: </span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="여러 줄을 추가해 보세요"></textarea>
<!-- 오류 발생! [vue/no-textarea-mustache]Unexpected mustache. Use 'v-model' instead. -->
<textarea>{{ message }}</textarea>
<!-- 올바른 사례 -->
<textarea v-model="message">33</textarea>
</template>
1-3. Checkbox
단일 체크박스는 불리언 값을 사용한다.
<script>
export default{
data() {
return {
checked: false
}
}
}
</script>
<template>
<input type="checkbox" id="checkbox" v-model="checked"/>
<label for="checkbox">{{ checked }}</label>
</template>
실행화면
배열 또는 Set에 여러 개의 체크박스 값을 바인딩할 수도 있다.
// Options API ver
<script>
export default{
data() {
return {
checkedNames: []
}
}
}
</script>
<template>
<div>체크된 이름: {{ checkedNames }}</div>
<input type="checkbox" id="cheddar" value="체다" v-model="checkedNames">
<label for="cheddar">체다</label>
<input type="checkbox" id="bibi" value="비비" v-model="checkedNames">
<label for="bibi">비비</label>
</template>
// Composition API ver
const checkedNames = ref([])
실행화면
이 경우 checkedNames 배열은 항상 현재 체크된 순서대로 값을 포함한다.
1-4. Radio
<script>
export default{
data() {
return {
picked: ''
}
}
}
</script>
<template>
<div>선택한 것: {{ picked }}</div>
<input type="radio" id="cheddar" value="체다" v-model="picked">
<label for="cheddar">체다</label>
<input type="radio" id="bibi" value="비비" v-model="picked">
<label for="bibi">비비</label>
</template>
실행화면
1-5. Select
a. 단일 셀렉트
<script>
export default{
data() {
return {
selected: ''
}
}
}
</script>
<template>
<div>선택됨: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">다음 중 하나를 선택하세요</option>
<option>고양이</option>
<option>강아지</option>
<option>뚱냥이</option>
</select>
</template>
실행화면
참고!
v-model 표현식의 초기 값이 옵션과 일치하지 않으면 <'select> 엘리먼트가 "선택되지 않은" 상태로 렌더링된다. ios에서는 이 경우 변경 이벤트를 발생시키지 않기 때문에 사용자가 첫 번째 항목을 선택할 수 없게 된다. 따라서 위의 예제처럼 비활성화된 옵션에 빈 값을 제공하는 것이 좋다.
오류 예제
<script>
export default{
data() {
return {
selected: 'value' // 초기값, 옵션과 불일치
}
}
}
</script>
<template>
<div>선택됨: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">다음 중 하나를 선택하세요</option>
<option value="cat">고양이</option>
<option value="dog">강아지</option>
<option value="caaaaaaat">뚱냥이</option>
</select>
</template>
b. 다중 선택(배열로 바인딩 됨)
<script>
export default{
data() {
return {
selected: 'value'
}
}
}
</script>
<template>
<div>선택됨: {{ selected }}</div>
<select v-model="selected" multiple>
<option value="cat">고양이</option>
<option value="dog">강아지</option>
<option value="caaaaaaat">뚱냥이</option>
</select>
</template>
실행화면
셀렉트 옵션은 v-for로 동적으로 렌더링할 수 있다.
// Options API ver
<script>
export default{
data() {
return {
selected: '1',
options: [
{text: '일초라도 안보이면', value:'1'},
{text: '이렇게 궁금한데', value:'2'},
{text: '삼초는 어떻게 기다려', value:'3'},
]
}
}
}
</script>
<template>
<select v-model="selected">
<option v-for="option in options" :value="option.value" :key="option">
{{ option.text }}
</option>
</select>
<div>선택됨: {{ selected }}</div>
</template>
// Composition API ver
const selected = ref('1')
const options = ref([
{ text: '하나', value: '1' },
{ text: '둘', value: '2' },
{ text: '셋', value: '3' },
])
2. Value Bindings
라디오, 체크박스 및 셀렉트 옵션의 경우, v-model에 바인딩된 값은 일반적으로 정적 문자열(체크박스의 경우 불리언)이다.
그러나 때로는 현재 활성 인스턴스의 동적 속성에 값을 바인딩하고 싶을 수도 있다. 이를 구현하기 위해서는 v-bind를 사용해야 한다. 또한 v-bind를 사용하면 입력 값을 문자열이 아닌 값에 바인딩 할 수 있다.
2-1. Checkbox
<script>
export default{
data() {
return {
toggle: '네'
}
}
}
</script>
<template>
<div>{{ toggle }}</div>
<input type="checkbox" v-model="toggle" true-value="네" false-value="아니오"/>
</template>
실행화면
true-value 및 false-value 속성은 v-model을 사용하는 경우에만 작동하는 Vue 전용 속성이다. 위 예제에서 toggle 속성의 값은 체크박스가 선택되면 네로 설정되고 선택되지 않을 때는 아니오로 설정된다. 이 전용 속성에 v-bind를 사용하여 동적으로 값을 바인딩할 수도 있다.
<script>
export default{
data() {
return {
toggle: 'default',
dynamicTrueValue: 'on',
dynamicFalseValue: 'off'
}
}
}
</script>
<template>
<div>{{ toggle }}</div>
<input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue"/>
</template>
tip!
브라우저는 폼 제출 시 체크되지 않은 상자는 포함하지 않기 때문에, true-value와 false-value 속성은 입력의 value 속성에 영향을 주지 않는다. 두 값 중 하나가 폼에 제출되도록 하려면 체크박스 대신 라디오로 구현해야 한다.
2-2. Radio
<script>
export default{
data() {
return {
pick: 'default',
first: 'op1',
second: 'op2'
}
}
}
</script>
<template>
<div>{{ pick }}</div>
<input type="radio" v-model="pick" :value="first"/>
<input type="radio" v-model="pick" :value="second"/>
</template>
실행화면
2-3. Select Options
<script>
export default{
data() {
return {
selected: 'default'
}
}
}
</script>
<template>
<div>{{ selected }}</div>
<select v-model="selected">
<!-- 인라인 객체 리터럴 -->
<option :value="{ number: 123 }">123</option>
<option value="456">456</option>
<option value="789">789</option>
</select>
</template>
3. Modifiers
3-1. .lazy
기본적으로 v-model은 각 input 이벤트 이후 데이터와 입력을 동기화한다. 대신 change 이벤트 이후에 동기화하기 위해 .lazy 수식어를 추가할 수 있다.
<script>
export default{
data() {
return {
msg: ''
}
}
}
</script>
<template>
<!-- 입력 필드에 값을 입력한 후 다른곳을 클릭하거나 포커스가 이동하는 change 이벤트가 발생하고,
그 시점에 변경되는 것을 확인 할 수 있다 -->
<div>값: {{ msg }}</div>
<input v-model.lazy="msg">
</template>
3-2. .number
사용자 입력이 자동으로 숫자로 유형 변환되도록 하려면, v-model 수식어로 .number를 추가하면 된다.
값을 parseFloat()로 파싱할 수 없으면 원래 값이 대신 사용된다.
인풋에 type="number"가 있으면 .number 수식어가 자동으로 적용된다.
<script>
export default{
data() {
return {
age: null
}
}
}
</script>
<template>
<div>값: {{ age }}</div>
<!--
숫자로 변환 가능한 문자열
1. 숫자 문자열 : "123"
2. 소수점을 포함한 숫자 문자열 : "45.67"
3. 지수 표기법을 사용한 숫자 문자열 : "1.23e4"
4. 공백을 포함하지 않은 숫자 문자열 : "123456789"
-->
<input v-model.number="age">
</template>
3-3. .trim
사용자 입력의 공백이 자동으로 트리밍되도록 하려면 v-model 수식어로 .trim을 추가하면 된다.
<script>
export default{
data() {
return {
msg: ''
}
}
}
</script>
<template>
<div>값: {{ msg }}</div>
<!-- 입력값의 앞뒤 공백을 제거한다. -->
<input v-model.trim="msg">
</template>
4. v-model with Components
HTML의 기본 입력 유형이 항상 사용자의 요구를 충족하는 것은 아니다. 다행히도 Vue 컴포넌트를 사용하면 완전히 사용자 정의된 동작으로 재사용 가능한 입력을 구축할 수 있다. 이러한 입력은 v-model에서도 작동한다. 추후에 나올 예정~
참고
'Frontend > Vue3' 카테고리의 다른 글
크롬 Vue.js devtools 사용 시 "Vue.js not detected" 오류 (0) | 2024.01.17 |
---|---|
[Vue 빠른시작] Lifecycle Hooks 예제 추가 (0) | 2024.01.17 |
[Vue 빠른시작] Event Handling 예제 추가 (0) | 2024.01.16 |
[Vue 빠른시작] List Rendering 예제 추가 (0) | 2024.01.16 |
[Vue 빠른시작] 조건부 렌더링(Conditional Rendering) 예제 추가 (0) | 2024.01.15 |