自定义组件使用v-model,太好用了

场景描述

scss 复制代码
我们在一个系统中,会出现这样的情况,
有一个联系人的下拉框,这个下拉框中的数据是从服务端获取的。
在很多页面都需要使用这个联系人(下拉框)。
我们通常是这样做的:
写一个下拉框组件然后调用接口。
这样不仅会造成代码冗余,而且不利于后期的维护。
比如说:如果有一天这个要发生变化,如果整个系统中有几十处。
难道我们要更改几十处?不会吧。这样好难受呀。
有的小伙伴说:这个还不简单:封装成为一个组件呀。

封装成为组件

xml 复制代码
concatPersonSelect.vue

<template>
  <el-form ref="form" :model="form" label-width="80px">
    <el-form-item label="联系人">
      <el-select v-model="form.person" placeholder="请选择活动区域">
        <el-option v-for="(item,index) in listAtt" :key="index" :label="item.name" :value="item.id"></el-option>
      </el-select>
    </el-form-item>
</el-form>
</template>
<script>
  export default {
    data() {
      return {
        listAtt:[],
        form: {
          person: '',
        }
      }
    },
    created() {
      this.getUserList()
    },
    methods: {
      // 模拟接口
      getUserList(){
        setTimeout(()=>{
          this.listAtt = [{
            name:'张三1',id:1
          },{
            name:'张三2',id:2
          },{
            name:'张三3',id:3
          },{
            name:'张三4',id:4
          }]
        },1600)
      }
    },
   
  }
</script>

页面使用

xml 复制代码
<template>
  <div>
    <h2>自定义组件也可以使用v-model</h2>
    <concatPersonSelect></concatPersonSelect>
  </div>
</template>
<script>
export default {
  components:{
    concatPersonSelect
  },
  data(){
    return{
      
    }
  }
}
</script>

现在遇见的问题

csharp 复制代码
是的,现在我们是把它封装成为了一个组件。
但是如果获取组件中的值呢?
这........,思考一会。
机智的小伙伴说:子组件提供一个方法,父组件通过ref的方式去调用。
那如果要赋值呢?子组件也提供一个方法,父组件去使用。
这样就可以获取值,赋值了。(耶,简直是一个天才)
这样虽然可以,但是真的优点繁琐,还有其他好的方法吗?
我们可以使用v-model,是的,我们给自己封装的组件使用v-model

组件中使用v-model的想法

kotlin 复制代码
我们给下拉框绑定一个 change 事件,这样值发生变化后。
通过 this.$emit 去更新。
与此同时,v-model中的值是 data中的 userValue 值。
userValue 中的值是从props中来的。
然后使用 model 中的 event 属性与 emit 事件保持一致

改造组件,组件可以使用 v-model

xml 复制代码
<template>
  <el-form ref="form"  label-width="80px">
    <el-form-item label="联系人">
      <el-select @change="changeGetValue" v-model="userValue" placeholder="请选择活动区域">
        <el-option v-for="(item,index) in listAtt" :key="index" :label="item.name" :value="item.id"></el-option>
      </el-select>
    </el-form-item>
</el-form>
</template>
<script>
  export default {
    model: {
      event: 'input-change', // 这个事件与下面的emit事件与之对应
      prop: 'propsInfoValue' // 
    },
    props:{
      // 父组件传递的值,用于数据回填
      propsInfoValue:{
        type:String,
        default:()=>{
          return ''
        }
      }
    }, 
    data() {
      return {
        listAtt:[],
        userValue: this.propsInfoValue
      }
    },
    created() {
      this.getUserList()
    },
    methods: {
      // 模拟接口
      getUserList(){
        setTimeout(()=>{
          this.listAtt = [{
            name:'张三1',id:'1'
          },{
            name:'张三2',id:'2'
          },{
            name:'张三3',id:'3'
          },{
            name:'张三4',id:'4'
          }]
        },1600)
      },
      // 值发生变化会被触发,就去更新
      changeGetValue(){
        this.$emit('input-change', this.userValue)
      }
    }
  }
</script>

页面就可以使用 v-model 了

javascript 复制代码
<template>
  <div>
    <h2>自定义组件也可以使用v-model</h2>
    <concatPersonSelect v-model="obj.name" ></concatPersonSelect>
    <el-button @click="getHandler"> 获取值 </el-button>
  </div>
</template>
import concatPersonSelect from '../components/concatPersonSelect/concatPersonSelect.vue'
export default {
  components:{
    concatPersonSelect
  },
  data(){
    return{
      obj:{
        name:''
      }
    }
  },
  methods: {
    getHandler(){
      console.log('获取的值', this.obj)
    }
  }
}
csharp 复制代码
现在我们可以获取组件中的值了。
并且不需要通过ref这样的方式来获取。
这样一来,更加的方便了。

如果有必填参数怎么搞?

ini 复制代码
我们都知道,如果某一个参数是必填的话。
在elementui中的form表单中。
需要同时满足两个条件,那就是:
rules 属性传入约定的验证规则,
并将 Form-Item 的 prop 属性设置为需校验的字段名即可。
因此,子组件中的 el-form-item中应该有一个 prop属性,
并且这个属性的值与 rules属性中的规则字段相同就行。
我们打算在子组件中使用  
v-bind="$attrs" 和 inheritAttrs: false,
这样页面中的参数可以直接进行传递

更新优化子组件

xml 复制代码
<template>
  <div>
    <el-form-item label="联系人"  v-bind="$attrs">
      <el-select @change="changeGetValue" v-model="userValue" placeholder="请选择联系人">
        <el-option v-for="(item,index) in listAtt" :key="index" :label="item.name" :value="item.id"></el-option>
      </el-select>
    </el-form-item>
  </div>
</template>
<script>
  export default {
    model: {
      event: 'input-change', // 这个事件与下面的emit事件与之对应
      prop: 'propsInfoValue' // 
    },
    props:{
      // 父组件传递的值,用于数据回填
      propsInfoValue:{
        type:String,
        default:()=>{
          return ''
        }
      }
    }, 
    inheritAttrs: false, //不让属性直接渲染在根节点上
    data() {
      return {
        listAtt:[],
        userValue: this.propsInfoValue
      }
    },
    created() {
      this.getUserList()
    },
    methods: {
      // 模拟接口
      getUserList(){
        setTimeout(()=>{
          this.listAtt = [{
            name:'张三1',id:'1'
          },{
            name:'张三2',id:'2'
          },{
            name:'张三3',id:'3'
          },{
            name:'张三4',id:'4'
          }]
        },1600)
      },
      // 值发生变化会被触发,就去更新
      changeGetValue(){
        this.$emit('input-change', this.userValue)
      }
    },
   
  }
</script>

页面使用

xml 复制代码
<template>
  <div>
    <h2>自定义组件也可以使用v-model</h2>

    <el-form :rules="rules" :model="ruleForm" ref="form"  label-width="80px">
      <el-form-item label="活动名称" prop="activeName">
        <el-input v-model="ruleForm.activeName"></el-input>
      </el-form-item>

      <!-- prop中的值必须与验证规则中的字段保持一致,否者无法验证 -->
      <concatPersonSelect prop="personName" v-model="ruleForm.personName" >
      </concatPersonSelect>

      <el-button @click="getHandler"> 获取值 </el-button>
    </el-form>
    
  </div>
</template>

<script>
export default {
  components:{
    concatPersonSelect
  },
  data(){
    return{
      // 表单中的字段
      ruleForm:{
        activeName:'',
        personName:''
      },
      // 验证规则以及提示语
      rules: {
        activeName: [
          { required: true, message: '请输入活动名称', trigger: 'blur' },
        ],
        personName: [
          { required: true, message: '请选联系人', trigger: 'change' }
        ]
      }
    }
  },
  methods: {
    getHandler(){
      console.log('获取的值', this.ruleForm)
      
      this.$refs['form'].validate((valid) => {
        // 验证规则成功
        console.log(1,valid)
      })
    }
  }
}
</script>

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax