Change of template instruction - v-model instruction

Keywords: Vue

Changes in v-model instructions

There are two similar functions in vue2, sync modifier and v-model, but they are often confused in practical application because they have similar functions. For example, I want to bind a dynamic value and try to change it in the sub component. The sync modifier and v-model can realize this function, so I don't know which to choose.

In vue3, the two functions are combined once. It removes the sync modifier directly and unifies it into the form of v-model parameters;

Change:

  • Incompatible: when used for custom components, the v-model property and event default name are changed

    prop: value----->modelValue

    event: input---->update:modelValue

  • The sync modifier of incompatible: v-bind and the model option of the component are removed and replaced with v-model
  • New: multiple v-model s can be used for bidirectional binding on the same component
  • New: you can customize the v-model modifier

1> Using a single v-model in a component

In vue3, the v-model on the custom component is equivalent to passing the modelValue attribute and receiving the thrown update:modelValue event:

app.vue

<template>
   <div>
   app
   <child v-model="counter"></child>  
   <!--Equivalent to-->
   <!-- <child :modelValue="counter" @updata:modeValue="counter=$event"></child> -->
   </div>
</template>
<script>
import child from "./components/child.vue"
import {ref} from "vue"
   export default {
      components:{
        child
      },
      setup(){
         const counter=ref(10)
         return {counter}
      }
   }
</script>

<style scoped>

</style>

 child.vue

<template>
    <div>
      {{modelValue}}
      <hr/>
      <input type="text" :value="modelValue"  @input="$emit('update:modelValue',$event.target.value)"/>
    </div>
</template>

<script>
    export default {
        props:{
            modelValue:{
                type:Number,
                default:0
            }
        }
    }
</script>

<style scoped>

</style>

2> To change the model name instead of changing the model option in the component, we need to pass an argument to the model

app.vue

<template>
   <div>
   app
   <child v-model:counter="counter"></child>  
   <!--Equivalent to-->
   <!-- <child :counter="counter" @updata:counter="counter=$event"></child> -->
   </div>
</template>

 child.vue

<template>
    <div>
      {{counter}}
      <hr/>
      <input type="text" :value="counter"  @input="$emit('update:counter',$event.target.value)"/>
    </div>
</template>

<script>
    export default {
        props:{
            counter:{
                type:Number,
                default:0
            }
        }
    }
</script>

 

  3> Use multiple v-model s on custom components

app.vue

<template>
   <div>
   app
   <child v-model:counter="counter" v-model:title="title"></child>  
   <!--Equivalent to-->
   <!-- <child :counter="counter" @updata:counter="counter=$event"
      :title="title"
      @updata:title="title=$event"
   ></child> -->
   </div>
</template>
<script>
import child from "./components/child.vue"
import {ref} from "vue"
   export default {
      components:{
        child
      },
      setup(){
         const counter=ref(10)
         const title=ref("Use multiple on custom components v-model")
         return {counter,title}
      }
   }
</script>

<style scoped>

</style>

 child.vue

<template>
    <div>
      {{counter}}
      {{title}}
      <hr/>
      <input type="text" :value="counter"  @input="$emit('update:counter',$event.target.value)"/>
      <input type="text" :value="title"  @input="$emit('update:title',$event.target.value)"/>
    </div>
</template>

<script>
    export default {
        props:{
            counter:{
                type:Number,
                default:0
            },
            title:{
                type:String

            }
        }
    }
</script>

<style scoped>

</style>

 

  Here we will receive a warning

 

  Type selection failed. couter should receive a number, but the result is a string. Because here we get the value through the text box and the string type, we need to convert it into number type with parseInt

<template>
    <div>
      {{counter}}
      {{title}}
      <hr/>
      <input type="text" :value="counter"  @input="$emit('update:counter',parseInt($event.target.value))"/>
      <input type="text" :value="title"  @input="$emit('update:title',parseInt($event.target.value))"/>
    </div>
</template>

  There will be no warning at this time

4> V-model custom modifier

Custom modifiers are supported in vue3. When we use form input binding, we see that v-model has built-in modifiers -. trim,. number,. lazy. However, in some cases, we may need to add our own custom modifiers.

Case:

Create a custom modifier cap that capitalizes the first letter of the string provided by the v-model binding

In vue3, the modifier added to the component v-model will be provided to the component through modelModifiers. In this case, we need to create a component that contains the modelModifiers attribute of the null object by default

Note: when triggered in the component's setup, the modelModifiers property contains cap, and its value is true.

cap.vue

<template>
    <div>
        <input type="text"
        :value="modelValue"
        @input="$emit('update:modelValue',$event.target.value)"/>
    </div>
</template>
<script>
import { getCurrentInstance } from 'vue'
export default {
    props:{
        modelValue:String,
         //The component receives the property modelModifiers object. The object will contain multiple modifiers. Here, an empty object {}
        modelModifiers:{
            default:()=>{}
        }
    },
    setup(props){
        const {ctx}=getCurrentInstance()
        console.log(ctx.modelModifiers);  //{capitalize:true}
    }
}
</script> 

 app.vue

<template>
   <div>
   app
   <cap v-model.capitalize="modelValue"></cap>
   </div>
</template>
<script>
import cap from "./components/cap.vue"
import {ref} from "vue"
   export default {
      components:{
        cap
      },
      setup(){
         const test=ref('hello')
         return {test}
      }
   }
</script>

 

  In the case, the component receives an attribute modelModifiers object, and then obtains the modifier capitalize value in setup as true, implements the modifier capitalize, and capitalizes the first letter of the value bound by v-model

cap.vue

<template>
    <div>
        <input type="text"
        :value="modelValue"
        @input="emitValue"/>
    </div>
</template>
<script>
import { getCurrentInstance } from 'vue'
export default {
    props:{
        modelValue:String,
         //The component receives the property modelModifiers object. The object will contain multiple modifiers. Here, an empty object {}
        modelModifiers:{
            default:()=>{}
        }
    },
   setup(props){
        const {ctx}=getCurrentInstance()
        console.log(ctx.modelModifiers);
        function emitValue(e){
           let v=e.target.value;
           if(ctx.modelModifiers.capitalize){
               v=v.charAt(0).toUpperCase()+v.slice(1)
           }
           ctx.$emit('update:modelValue',v)
        }
        return {emitValue}

    }
}
</script>

  At this point, we are entering any letter, and the first letter is capitalized by default

vue3 supports components to bind multiple v-model s with parameters at the same time. Therefore, in the case of parameters, the modifier attribute name becomes the parameter name +'Modifiers'

app.vue

<template>
   <div>
   app

   <cap v-model.capitalize="test"></cap>
   <hr/>
   <all v-model:title.capitalize="title" v-model:test.capitalize="title"></all>
   </div>
</template>
<script>

import cap from "./components/cap.vue"
import all from "./components/all.vue"
import {ref} from "vue"
   export default {
      components:{

        cap,
        all
      },
      setup(){
         const counter=ref(10)
         const title=ref("title")
         const test=ref('hello')
         return {counter,title,test}
      }
   }
</script>

<style scoped>

</style>

 all.vue

<template>
    <div>
       title: <input type="text" :value="title" @input="$emit('update:title',$event.target.value)">
       test: <input type="text" :value="test" @input="$emit('update:test',$event.target.value)">
    </div>
</template>
<script>
import { getCurrentInstance } from '@vue/runtime-core'
    export default {
        props:['title','titleModifiers','test','testModifiers'],
        setup(){
            const {ctx}=getCurrentInstance();
            console.log(ctx.titleModifiers,"Multiple v-model");
            console.log(ctx.testModifiers);
        }                                                  
    }
</script>

<style scoped>

</style>

 

 app.vue

<template>
   <div>
   app
   <cap v-model.capitalize="test"></cap>
   <hr/>
    <all  v-model:title.capitalize="title"   v-model:test.upper="test" ></all>
   </div>
</template>
<script>

import cap from "./components/cap.vue"
import all from "./components/all.vue"
import {ref} from "vue"
   export default {
      components:{

        cap,
        all
      },
      setup(){
         const counter=ref(10)
         const title=ref("title")
         const test=ref('hello')
         return {counter,title,test}
      }
   }
</script>

<style scoped>

</style>

 all.vue

<template>
    <div>
        <p>
            title:
            <input type="text"
            :value="title"
            @input="emitCapitalize"/>
        </p>
        <p>
            test:
            <input type="text"
            :value="test"
            @input="emitUpper"/>
        </p>
    </div>
</template>
<script>
import { getCurrentInstance } from 'vue'
export default {
    props:['title','titleModifiers','test','testModifiers'],
    setup(props){
      const {ctx}=getCurrentInstance();
      //console.log(ctx.titleModifiers, "multiple v-model s");
      //console.log(ctx.testModifiers, "multiple v-model s");
    //   title case
      function emitCapitalize(e){
           let v=e.target.value;
           if(ctx.titleModifiers.capitalize){
               v=v.charAt(0).toUpperCase()+v.slice(1)
           }
           ctx.$emit('update:title',v)
      }

    //   All capital letters
    function emitUpper(e){
        let v=e.target.value;
        if(ctx.testModifiers.upper){
            v=v.toUpperCase()
        }
        ctx.$emit('update:test',v)
    }

      return {emitCapitalize,emitUpper}
    }
}
</script>

 

Posted by ScubaKing22 on Wed, 10 Nov 2021 09:04:42 -0800