Refer to the element source code to write an input controlled component with vue

Refer to the element source code to write an input controlled component with vue


theme: cyanosis highlight: atom-one-dark

In React, form elements

input
Set in
value
, It is a controlled component, through
onChange
In the event
setState()
change
value
Value to update
state
Value and the value rendered in the DOM. But in vue, the form element settings
value
Value even if
value
The value has changed, in the dom
value
The performance is also the same as in the data
value
Inconsistent

The difference between controlled components in vue and react

In HTML, form elements (such as

<input>
,
<textarea>
with
<select>
) Usually maintain it yourself
state
, And update based on user input. In React, mutable state is usually stored in the component s
state
Properties, and can only be used by
setState()
To update.

We can combine the two to make React's

state
Become the "only data source". The React component that renders the form also controls the actions that occur on the form during user input. Form input elements whose values are controlled by React in this way are called "controlled components".

The above source is the react document-controlled component

import React, {useState} from "react" ; export default function App () { const [vale, setValue] = useState( 0 ); const changeEvent = function ( e ) { console .log(e); setValue( 123 ); //value and rendered value are both 123 } return ( < div className = "App" > < input type = "text" value = {value} onChange = {changeEvent}/> </div > ); } Copy code

And in vue, change

value
Value, only
data
The state in the DOM has changed, and the state in the native DOM
value
The value has not been changed, and the final rendered value is still the value entered by the user

<template> < input :value = "value" @ input = "setValue"/> </template> < script > export default { name : "App" , components : {}, data : function ( ) { return { value : '' , }; }, methods : { setValue ( e ) { this .value = 123 //Only the value of value in data has changed, and the value rendered in the DOM is still the input value } } }; </script > copy code

Use vue to write an input controlled component

In daily business, the requirements of controlled components are often used to limit the input of the input box, such as a number that can only enter numbers

input
frame. When using elementUI, I found its
<el-input>
As a controlled component, I went to elementUI-github to see how this operation is implemented.

The core principle is to update oneself

data
At the same time, update the original input DOM together
value
.

code show as below:

First write one exactly like an ordinary

<input>
Used as elements
<CtrlInput>
Component

<template> < label > < input v-bind = "$attrs" :value = "value" v-on = "inputListeners"/> </label > </template> < script > export default { name : "CtrlInput" , props : { value : { type : String } }, computed : { inputListeners () { return { //Add all listeners from the parent ...this.$listeners, //Then we add a custom listener //Here to ensure that the component works with `v-model` input : e => { this .$emit( "input" , e.target.value); } }; } } }; </script > < style scoped > </style > Copy code

All with native

input
identical
attribute
And listener can work normally, and ensure that the components cooperate
v-model
Can also work

Then in

input
Listener, set
nativeInputValue
(Native DOM
value
Value) and
data
middle
value
The same is fine.

<template> < label > < input ref = "input" v-bind = "$attrs" :value = "value" v-on = "inputListeners"/> </label > </template> < script > export default { name : "CtrlInput" , props : { value : { type : String } }, computed : { inputListeners () { return { //Add all listeners from the parent ...this.$listeners, //Then we add a custom listener //Here to ensure that the component works with `v-model` input : e => { this .$emit( "input" , e.target.value); //Ensure native input value is controllable //ensure native input value is controlled this .$nextTick( this .setNativeInputValue); } }; }, nativeInputValue () { //Convert the incoming value to String to prevent errors return this .value === null || this .value === undefined ? "" : String ( this .value); } }, methods : { setNativeInputValue () { //Keep the displayed native input value consistent with the input value in this const input = this .$refs.input; if (!input) return ; if (input.value === this .nativeInputValue) return ; input.value = this .nativeInputValue; } }, watch : { //Keep the displayed native input value consistent with the input value in this nativeInputValue () { this .setNativeInputValue(); } } }; </script > < style scoped > </style > Copy code

this.$nextTick(this.setNativeInputValue);
This line of code means
data
middle
value
After the change, and the rendering is complete (use
$nextTick
) Change again
nativeInputValue
The value of, you can make the native DOM consistent with its own state

use

Requirement: An input box that can only enter numbers, and other characters will not be displayed

<template> < div > Controlled components that can only enter numbers < CtrlInput type = "text" placeholder = "Please enter a number" :value = "value" @ input = "handleInput" > </CtrlInput > </div > </template> < script > import CtrlInput from "./CtrlInput" ; export default { name : "Demo" , components : { CtrlInput }, data () { return { value : "" }; }, methods : { handleInput ( v ) { if ( /^[0-9]*$/ .test(v)) { this .value = v; } } } }; </script > copy code

reference: