Vue:表单与 v-model

官方文档

基础用法

你可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定

它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

v-model 会忽略所有表单元素的 valuecheckedselected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现 v-model 不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用 input 事件。

input 文本

<template>
  <div id="app">
    <input v-model="message" placeholder="edit me">
    <p>Message is: {{ message }}</p>
    <p><button @click="message = 'river'">set message to river</button></p>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      message: 'hi'
    }
  },
  components: {}
};
</script>
复制代码

74.1.1.gif

点击button,改内存,页面会自动变化。改页面的东西,内存也会自动变化。这就是双向数据绑定

textarea 多行文本

<template>
  <div id="app">
    <textarea v-model="message" placeholder="edit me" />
    <p>Message is: {{ message }}</p>
    <p><button @click="message = 'river'">set message to river</button></p>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      message: 'hi'
    }
  },
  components: {}
};
</script>
复制代码

在文本区域插值 (<textarea>{{text}}</textarea>) 并不会生效,应用 v-model 来代替。

74.1.2.gif

checkbox 复选框

单个复选框

<template>
  <div id="app">
    <label>
      <input type="checkbox" v-model="x">
      <span>x:{{ x }}</span>
    </label>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      x: true
    }
  },
  components: {}
};
</script>
复制代码

单个复选框,绑定到布尔值

74.1.3.gif

多个复选框

<template>
  <div id="app">
    爱好:{{x}},
    <label>
      <input type="checkbox" v-model="x" :value="1">
      <span>抽烟</span>
    </label>
    <label>
      <input type="checkbox" v-model="x" :value="2">
      <span>喝酒</span>
    </label>
    <label>
      <input type="checkbox" v-model="x" :value="3">
      <span>烫头</span>
    </label>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      x: []
    }
  },
  components: {}
};
</script>
复制代码

多个复选框,绑定到同一个数组

74.1.4.gif

radio 单选按钮

<template>
  <div id="app">
    你想要:{{x}},
    <label>
      <input name="want" type="radio" v-model="x" :value="1">
      <span>抽烟</span>
    </label>
    <label>
      <input name="want" type="radio" v-model="x" :value="2">
      <span>喝酒</span>
    </label>
    <label>
      <input name="want" type="radio" v-model="x" :value="3">
      <span>烫头</span>
    </label>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      x: []
    }
  },
  components: {}
};
</script>
复制代码

74.1.5.gif

select 选择框

单选时

<template>
  <div id="app">
    你想要:{{x}}
    <hr/>
    <select v-model="x">
      <option value=""> - </option>
      <option value="1">抽烟</option>
      <option value="2">喝酒</option>
      <option value="3">烫头</option>
    </select>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      x: []
    }
  },
  components: {}
};
</script>
复制代码

如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

74.1.6.gif

v-for渲染的动态选项(多选也一样)

<template>
  <div id="app">
    你想要:{{x}}
    <hr/>
    <select v-model="x">
      <option value=""> - </option>
      <option v-for="item in array" :value="item.value" :key="item.value">{{item.text}}</option>
    </select>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      array: [
        {text:'抽烟', value:1},
        {text:'喝酒', value:2},
        {text:'烫头', value:3}
      ],
      x: ""
    }
  },
  components: {}
};
</script>
复制代码

多选时

绑定到一个数组

<template>
  <div id="app">
    你想要:{{x}}
    <hr/>
    <select multiple v-model="x">
      <option value=""> - </option>
      <option v-for="item in array" :value="item.value" :key="item.value">{{item.text}}</option>
    </select>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      array: [
        {text:'抽烟', value:1},
        {text:'喝酒', value:2},
        {text:'烫头', value:3}
      ],
      x: []
    }
  },
  components: {}
};
</script>
复制代码

+shift连选,+ctrl多选,再按一次+ctrl取消(因为很多用户不会这样用,所以一般不这样做多选框)

74.1.7.gif

form

<template>
  <div id="app">
    登录
    <form @submit.prevent="onSubmit">
      <label>
        <span>用户名</span>
        <input type="text" v-model="user.username" />
      </label>
      <label>
        <span>密码</span>
        <input type="password" v-model="user.password" />
      </label>
      <button type="submit">登录</button>
    </form>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      user:{
        username: '',
        password: ''
      }
    }
  },
  methods:{
    onSubmit(){
      console.log(this.user);
    }
  },
  components: {}
};
</script>
复制代码

74.1.8.gif


修饰符

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步。

回顾下input事件和change事件

  • input事件,键盘、鼠标、任何输入设备的输入
  • change事件,只在 input 失去焦点时触发
// 比如将上面这个例子的 <input /> 加上 .lazy
<input type="text" v-model="user.username" />
复制代码

加之前

74.2.1.gif

加之后

74.2.2.gif

.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符。

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

// 还是改上面那个例子
<template>
  <input type="text" v-model.number="user.username" />
  // input 加 .number 修饰符
  ...
</template>

<script>
export default {
  ...
  data() {
    ...
      user:{
        username: 0,
    	// username 改为 number 类型
        password: ''
      }
    }
  ...
</script>
复制代码

返回效果

74.2.3.gif

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符。

// 改上面例子
<input type="text" v-model.trim="user.username" />
复制代码

改前

74.2.4.gif

改后

74.2.5.gif


自定义组件的 v-model

官方文档

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件。

例子:

// 还是上面的例子 App.vue
<template>
  <div id="app">
    {{user}}
    <hr />
    登录
    <form @submit.prevent="onSubmit">
      <label>
        <span>用户名</span>
        <MyInput v-model="user.username" />
        <MyInput :value="user.username" @input="user.username = $event" /> // 相当于使用 v-model
      </label>
      <label>
        <span>密码</span>
        <input type="password" v-model="user.password" />
      </label>
      <button type="submit">登录</button>
    </form>
  </div>
</template>

<script>
import MyInput from "@/MyInput";
export default {
  name: 'App',
  components: {MyInput},
  data() {
    return {
      user:{
        username: '',
        password: ''
      }
    }
  },
  methods:{
    onSubmit(){
      console.log(this.user);
    }
  },
};
</script>
复制代码
// src 目录下再新建一个 MyInput.vue,将此组件导入上面组件
<template>
  <div class="red wrapper">
    <input :value="value" @input="$emit('input',$event.target.value)" />
  </div>
</template>

<script>
export default {
  name: 'MyInput',
  props: {
    value: {
      type: String
    }
  }
}
</script>

<style scoped>
  .red {
    background: red;
  }
  .wrapper {
    display: inline-block;
  }
</style>
复制代码

效果

74.3.1.gif


使用 ant-design-vue

Ant Design 分别提供了 Vue 和 React 的UI组件库

github

官方文档

安装 antd

yarn add ant-design-vue
复制代码

修改 main.js 引入 antd

// 完整引入
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';

Vue.use(Antd);
复制代码

官方也提供了局部导入组件的方法,在此不做赘述

使用组件

这里练习一下使用组件,以form表单为例

Form 表单-登录框

注意要将 XML 和 JS 的内容都引入,否则会报错。

修改后代码:

<template>
  <div id="app">
    <a-form
        id="components-form-demo-normal-login"
        :form="form"
        class="login-form"
        @submit="handleSubmit"
    >
      <a-form-item>
        <a-input
          v-decorator="[
          'username',
          { rules:
          [
            {require: true, message: '请填写用户名'}
          ]
          },
        ]"
            placeholder="用户名"
        >
          <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)" />
        </a-input>
      </a-form-item>
      <a-form-item>
        <a-input
          v-decorator="[
          'password',
          { rules:
          [
            { required: true, message: '请填写密码' },
            { min: 8, message: '密码最少8个字符' },
            {pattern: /[a-zA-Z]/, message: '必须包含至少一个字母'}
          ]
          },
        ]"
            type="password"
            placeholder="密码"
        >
          <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)" />
        </a-input>
      </a-form-item>
      <a-button type="primary" html-type="submit" class="login-form-button">
        Log in
      </a-button>
    </a-form>
  </div>
</template>

<script>
export default {
  name: 'App',
  beforeCreate() {
    this.form = this.$form.createForm(this, { name: 'normal_login' });
  },
  methods: {
    handleSubmit(e){
      e.preventDefault();
      this.form.validateFields((err, values) => {
        if (!err) {
          console.log('Received values of form: ', values);
        }
      });
    }
  }
};
</script>
复制代码

效果:

74.4.1.gif

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享