Vue 表单输入绑定(2)

359 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1. v-model 绑定其它表单元素

除了绑定 type="text"<input> 元素,v-model 还可以绑定 <textarea> 元素、type="checkbox"<input> 元素、type="radio"<input> 元素以及 <select> 元素。

1.1 绑定 <textarea> 元素

与绑定 type="text"<input> 元素的做法类似:

<label for="introduction">
  自我介绍:
  <textarea id="introduction" v-model="intro" cols="30" rows="10"></textarea>
</label>
<h2>intro: {{ intro }}</h2>
data() {
  return {
    intro: 'Hello World'
  }
}

1.2 绑定 type="checkbox"<input> 元素

第一种情况是单选框,即只有一个 type="checkbox"<input> 元素,v-model 绑定之后的最终结果是一个布尔值(true/false):

<label for="agreement">
  <input id="agreement" type="checkbox" v-model="isAgree"> 同意协议
</label>
<h2>isAgree: {{ isAgree }}</h2>
data() {
  return {
    isAgree: false
  }
}

第二种情况是多选框,即有多个 type="checkbox"<input> 元素,可以选择多个值,这时这些 <input> 元素上的 v-model 需要绑定到同一个属性,但它们的 value 属性值则应该各不相同,因为选中的 <input> 元素的值就是这些 value 属性的值,并且最终会被加入到一个数组中去:

<span>爱好:</span>
<label for="basketball">
  <input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球
</label>
<label for="football">
  <input id="football" type="checkbox" v-model="hobbies" value="football"> 足球
</label>
<label for="volleyball">
  <input id="volleyball" type="checkbox" v-model="hobbies" value="volleyball"> 排球
</label>
<h2>hobbies: {{ hobbies }}</h2>
data() {
  return {
    hobbies: []
  }
}

补充:

<p>
  <input type="checkbox" id="sleep-cbox" v-model="isChecked">
  <label for="sleep-cbox">睡觉</label>
</p>
<h3>{{ isChecked }}</h3>
data() {
  return {
    isChecked: false
  }
}

等价于

<p>
  <input type="checkbox" id="sleep-cbox" :checked="isChecked" @input="onCheck">
  <label for="sleep-cbox">睡觉</label>
</p>
<h3>{{ isChecked }}</h3>
data() {
  return {
    isChecked: false
  }
},
methods: {
  onCheck(event) {
    this.isChecked = event.target.checked;
  }
}

再来一个清单应用案例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    [v-cloak] {
      display: none;
    }

    .done {
      color: gray;
      text-decoration: line-through;
    }
  </style>
</head>
<body>

  <div id="app" v-cloak>
    <!-- <h2>{{ title }}</h2> -->
    <input type="text" v-model="title" @keydown.enter="addTodo">
    <button v-if="active < all" @click="clear">清理</button>
    <div v-if="todos.length">
      <ul>
        <li v-for="todo in todos">
          <!-- <input :id="todo.title" type="checkbox" :checked="todo.done" @input="done"> -->
          <input :id="todo.title" type="checkbox" v-model="todo.done">
          <label :for="todo.title" :class="{ done: todo.done }">{{ todo.title }}</label>
        </li>
      </ul>
      <!-- <div>{{ todos.filter(v => !v.done).length }} / {{ todos.length }}</div> -->
      <div>
        <label for="all-check">
          全选<input type="checkbox" id="all-check" v-model="allDone">
        </label>
        <span>{{ active }} / {{ all }}</span>
      </div>
    </div>
    <div v-else>暂无数据</div>
  </div>
  
  <script src="https://unpkg.com/vue@next"></script>
  <script>
    const App = {
      data() {
        return {
          title: '',
          // todos: ['学习 Vue', '跑步']
          todos: [
            { title: '吃饭', done: true },
            { title: '学习', done: false },
            { title: '跑步', done: false }
          ]
        }
      },
      computed: {
        active() {
          return this.todos.filter(v => !v.done).length;
        },
        all() {
          return this.todos.length;
        },
        allDone: {
          get() {
            return this.active === 0;
          },
          set(val) {
            this.todos.forEach(todo => todo.done = val);
          }
        }
      },
      methods: {
        addTodo() {
          // this.todos.push(this.title);
          this.todos.push({ title: this.title, done: false });
          this.title = '';
        },
        done(event) {
          const target = this.todos.find(todo => todo.title === event.target.id);
          target.done = event.target.checked;
        },
        clear() {
          this.todos = this.todos.filter(todo => !todo.done);
        }
      }
    };

    // 启动应用
    Vue.createApp(App).mount('#app');
  </script>

</body>
</html>

效果展示:

清单应用案例

1.3 绑定 type="radio"<input> 元素

使用 type="radio"<input> 元素时,通常会使用两个,这两个 <input> 元素上的 v-model 的值也是绑定到同一个属性,而它们的 value 属性值也应该是“互斥”的。选中这两个 <input> 元素中的一个时,另一个就会变为不选中,此时,选中的那个 <input> 元素的 value 就会自动绑定到对应的属性中:

<span>性别:</span>
<label for="male">
  <input id="male" type="radio" v-model="gender" value="male"></label>
<label for="female">
  <input id="female" type="radio" v-model="gender" value="female"></label>
<h2>gender: {{ gender }}</h2>
data() {
  return {
    gender: 'male'
  }
}

1.4 绑定 <select> 元素

<select> 元素可以设置为单选,也可以设置为多选,但单选相对来说更常见。<select> 元素中会有多个 <option> 元素,每个 <option> 元素上的 value 属性应该设置对应的值,一旦选中某个 <option>,就会将它上面对应的 value 属性值绑定到 v-model 对应的属性里面:

<span>喜欢的水果:</span>
<select v-model="fruits">
  <!-- <select v-model="fruits" multiple size="2"> -->
  <option value="apple">苹果</option>
  <option value="banana">香蕉</option>
  <option value="peach">桃子</option>
</select>
<h2>fruits: {{ fruits }}</h2>
data() {
  return {
    fruits: 'apple'
  }
}

以上关于 v-model 绑定其它表单元素的案例完整代码如下(<body> 元素中的代码):

<div id="app"></div>

<template id="my-app">
  <!-- 1. 绑定 textarea -->
  <label for="introduction">
    自我介绍:
    <textarea id="introduction" v-model="intro" cols="30" rows="10"></textarea>
  </label>
  <h2>intro: {{ intro }}</h2>

  <!-- 2. 绑定 type="checkbox" 的 input -->
  <!-- 2.1 单选框 -->
  <label for="agreement">
    <input id="agreement" type="checkbox" v-model="isAgree"> 同意协议
  </label>
  <h2>isAgree: {{ isAgree }}</h2>
  <!-- 2.2 多选框 -->
  <span>爱好:</span>
  <label for="basketball">
    <input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球
  </label>
  <label for="football">
    <input id="football" type="checkbox" v-model="hobbies" value="football"> 足球
  </label>
  <label for="volleyball">
    <input id="volleyball" type="checkbox" v-model="hobbies" value="volleyball"> 排球
  </label>
  <h2>hobbies: {{ hobbies }}</h2>

  <!-- 3. 绑定 type="radio" 的 input -->
  <span>性别:</span>
  <label for="male">
    <input id="male" type="radio" v-model="gender" value="male"></label>
  <label for="female">
    <input id="female" type="radio" v-model="gender" value="female"></label>
  <h2>gender: {{ gender }}</h2>

  <!-- 4. 绑定 select -->
  <span>喜欢的水果:</span>
  <select v-model="fruits">
    <!-- <select v-model="fruits" multiple size="2"> -->
    <option value="apple">苹果</option>
    <option value="banana">香蕉</option>
    <option value="peach">桃子</option>
  </select>
  <h2>fruits: {{ fruits }}</h2>
</template>

<script src="./js/vue.js"></script>
<script>
  const App = {
    data() {
      return {
        intro: 'Hello World',
        isAgree: false,
        hobbies: [],
        gender: 'male',
        fruits: 'apple'
      }
    },
    template: '#my-app'
  };

  Vue.createApp(App).mount('#app');
</script>