[Vue 빠른시작] Form Input Bindings 예제 추가

프론트엔드에서 폼을 처리할 때, 폼 입력 엘리먼트의 상태를 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에서도 작동한다. 추후에 나올 예정~




참고

Vue Docs