第 6 章 表单与 v-model

表单类控件承载了一个网页数据的录入与交互,本章将介绍如何使用指令 v-model 完成表单的数据双向绑定。

基本用法

表单控件在实际业务较为常见,比如单选、多选、下拉选择、输入框等,用它们可以完成数据的录入、校验、提交等。 Vue.js 提供了 v-model 指令,用于在表单类元素上双向绑定数据,例如在输入框上使用时,输入的内容会实时映射到绑定的数据上。例如下面的例子:

  <body>
      <div id="app">
          <input type="text" v-model="message" placeholder="请输入..." />
          <p>输入的内容是:{{ message }}</p>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  message: ''
              }
          });
      </script>
  </body>

在输入框输入的同时,{{ message } }也会实时将内容渲染在视图中,如下图所示。



对于文本域 textarea 也是同样的用法:

  <body>
      <div id="app">
          <textarea v-model="text" placeholder="请输入..."></textarea>
          <p>输入的内容是:{{ text }}</p>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  text: ''
              }
          });
      </script>
  </body>

使用 v-model 后,表单控件显示的值只依赖所绑定的数据,不再关心初始化时的 value 属性,对于在<textarea></textarea> 之间插入的值,也不会生效. 使用 v-model 时,如果是用中文输入法输入中文,一般在没有选定饲组前,也就是在拼音阶段, Vue 是不会更新数据的,当敲下汉字时才会触发更新。 如果希望总是实时更新,可以用 @input 来替代 v-model. 事实上, v-model 也是一个特殊的语法糖,只不过它会在不同的表单上智能处理。例如下面的示例:

  <body>
      <div id="app">
          <input type="text" @input="handleInput" placeholder="输入..." />
          <p>输入的内容是:{{ message }}</p>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  message: ''
              },
              methods: {
                  handleInput: function(e){
                      this.message = e.target.value;
                  }
              }
          });
      </script>
  </body>

来看看更多的表单控件。

  • 单选按钮:
    单选按钮在单独使用时,不需要 v-model,直接使用 v-bind 绑定一个布尔类型的值, 为真时选中, 为否时不选,例如:
  <body>
      <div id="app">
          <input type="radio" :checked="picked" />
          <label>单选按钮</label>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  picked: true
              }
          });
      </script>
  </body>

如果是组合使用来实现互斥选择的效果,就需要 v-model 配合 value 来使用:

  <body>
      <div id="app">
          <input type="radio" v-model="picked" value="html" id="html" />
          <label for="html">HTML</label>
          <br />
          <input type="radio" v-model="picked" value="js" id="js" />
          <label for="js">JavaScript</label>
          <br />
          <input type="radio" v-model="picked" value="css" id="css" />
          <label for="css">CSS</label>
          <br />
          <p>选择的项是:{{ picked }}</p>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  picked: 'js'
              }
          });
      </script>
  </body>

数据 picked 的值与单选按钮的 value 值一致时,就会选中该项,所以当前状态下选中的是第二项 JavaScript,如下图所示。


  • 复选框:
    复选框也分单独使用和组合使用,不过用法稍与单选不同。复选框单独使用时,也是用 v-model 来绑定一个布尔值,例如:
  <body>
      <div id="app">
          <input type="checkbox" v-model="checked" id="checked" />
          <label for="checked">选择状态:{{ checked }}</label>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  checked: false
              }
          });
      </script>
  </body>

在勾选时,数据 checked 的值变为了 true, label 中渲染的内容也会更新。
组合使用时,也是 v-model 与 value 一起,多个勾选框都绑定到同一个数组类型的数据, value 的值在数组当中,就会选中这一项。这一过程也是双向的,在勾选时, value 的值也会自动 push 到这个数组中,示例代码如下:

 <body>
     <div id="app">
         <input type="checkbox" v-model="checked" value="html" id="html" />
         <label for="html">HTML</label>
         <br />
         <input type="checkbox" v-model="checked" value="js" id="js" />
         <label for="js">JavaScript</label>
         <br />
         <input type="checkbox" v-model="checked" value="css" id="css" />
         <label for="css">CSS</label>
         <br />
         <p>选择的项是:{{ checked }}</p>
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                 checked: ['js','css']
             }
         });
     </script>
 </body>

当前状态下的结果如图所示。


  • 选择列表:
    选择列表就是下拉选择器,也是常见的表单控件,同样也分为单选和多选两种方式。先看一下单选的示例代码:
 <body>
     <div id="app">
         <select v-model="selected">
            <option>html</option>
            <option value="js">JavaScript</option>
            <option>css</option>
        </select>
        <p>选择的项是:{{ selected }}</p>
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                 selected: 'html'
             }
         });
     </script>
 </body>

<option>是备选工页,如果含有 value 属性, v-model 就会优先匹配 value 的值: 如果没有, 就会直接匹配<option>的 text,比如选中第二项时, selected 的值是 js, 而不是 JavaScript。
给<selected>添加属性 multiple 就可以多选了, 此时 v-model 绑定的是一个数组, 与复选框用法类似,示例代码如下:

 <body>
     <div id="app">
         <select v-model="selected" multiple>
            <option>html</option>
            <option value="js">JavaScript</option>
            <option>css</option>
        </select>
        <p>选择的项是:{{ selected }}</p>
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                 selected: ['html','js']
             }
         });
     </script>
 </body>

在业务中,<option>经常用 v-for 动态输出, value 和 text 也是用 v-bind 来动态输出的, 例如:

 <body>
     <div id="app">
         <select v-model="selected">
            <option
                  v-for="option in options"
                  :value="option.value">{{ option.text }}</option>              
        </select>
        <p>选择的项是:{{ selected }}</p>
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                 selected: 'html',
                options: [
                    {
                        text: 'HTML',
                        value: 'html'
                    },
                    {
                        text: 'JavaScript',
                        value: 'js'
                    },
                    {
                        text: 'CSS',
                        value: 'css'
                    }
                ]
             }
         });
     </script>
 </body>

虽然用选择列表<select>控件可以很简单地完成下拉选择的需求,但是在实际业务中反而不常用,因为它的样式依赖平台和浏览器,无法统一, 也不太美观, 功能也受限, 比如不支持搜索,所以常见的解决方案是用 div 模拟一个类似的控件。当完成后面章节中组件的内容后, 可以尝试编写一个下拉选择器的通用组件。

绑定值

上一节介绍的单选按钮、复选框和选择列表在单独使用或单选的模式下, v-model 绑定的值是一个静态字符串或布尔值, 但在业务中,有时需要绑定一个动态的数据, 这时可以用 v-bind 来实现。

  • 单选按钮:
 <body>
     <div id="app">
         <input type="radio" v-model="picked" :value="value" />
        <label>单选按钮</label>
        <p>{{ picked }}</p>
        <p>{{ value }}</p>
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                 picked: false,
                value: 123
             }
         });
     </script>
 </body>

在选中时, app.picked === app.value, 值都是 123。

  • 复选框:
 <body>
     <div id="app">
         <input type="checkbox" v-model="toggle" :true-value="value1" :fales-value="value2"/>
        <label>复选框</label>
        <p>{{ toggle }}</p>
        <p>{{ value1 }}</p>
        <p>{{ value2 }}</p>
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                 toggle: false,
                value1: 'a',
                value2: 'b'
             }
         });
     </script>
 </body>

勾选时, app.toggle = app.valuel ; 未勾选时, app.toggle === app.value2。

  • 选择列表:
 <body>
     <div id="app">
         <select v-model="selected">
            <option :value="{ number: 123 }">123</option>
        </select>
        {{ selected.number }}
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                selected: ''
             }
         });
     </script>
 </body>

当选中时, app.selected 是一个 Object,所以 app.selected.number == 123。

修饰符

与事件的修饰符类似, v-model 也有修饰符,用于控制数据同步的时机。

  • .lazy:
    在输入框中, v-model 默认是在 input 事件中同步输入框的数据(除了提示中介绍的中文输入法情况外),使用修饰符 .lazy 会转变为在 change 事件中同步,示例代码如下:
 <body>
     <div id="app">
        <input type="text" v-model.lazy="message" />
        {{ message }}
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                message: ''
             }
         });
     </script>
 </body>

这时, message 并不是实时改变的,而是在失焦或按回车时才更新。

  • .number:
    使用修饰符.number 可以将输入转换为 Number 类型,否则虽然你输入的是数字,但它的类型其实是 String,比如在数字输入框时会比较有用,示例代码如下:
 <body>
     <div id="app">
        <input type="number" v-model.nnumber="message" />
        {{ typeof message }}
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                message: 123
             }
         });
     </script>
 </body>
  • .trim:
    修饰符 .trim 可以自动过滤输入的首尾空格,示例代码如下:
 <body>
     <div id="app">
        <input type="text" v-model.trim="message" />
        {{ message }}
     </div>
     <script>
         var app = new Vue({
             el: '#app',
             data: {
                message: ''
             }
         });
     </script>
 </body>

从 Vue. 2.x 开始, v-model 还可以用于自定义组件,满足定制化的需求, 在下一章会详细介绍。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容