Creando entradas personalizadas con Vue.js
En este tutorial, Joseph Zimmerman pretende ayudarle a comprender cómo funciona v-model en entradas nativas y en componentes personalizados de forma predeterminada. Además, aprenderá a crear casillas de verificación y radios personalizados que emulen cómo funciona v-model en ellos de forma nativa.
En particular, las entradas de formulario tienden a tener mucha complejidad que conviene ocultar en un componente, como diseños personalizados , etiquetas, validación, mensajes de ayuda y asegurarse de que cada una de estas piezas esté en el orden correcto para que se representen. correctamente.
Además de eso, Vue tiene una directiva incorporada llamada v-model
que simula un enlace bidireccional vinculando un valor y capturando eventos de entrada. Si va a crear un componente de entrada personalizado, definitivamente querrá admitir la v-model
directiva.
Lamentablemente, cuando busqué ejemplos de entradas personalizadas en Vue para botones de opción o casillas de verificación, no las tomaron v-model
en consideración en absoluto o no las implementaron correctamente. Existe documentación decente para entradas de texto personalizadas , pero como no explica la personalización de radios o casillas de verificación, lo discutiremos aquí.
Escribir módulos reutilizables en ES6
¿Está entusiasmado por aprovechar las nuevas funciones del lenguaje JavaScript pero no está seguro de por dónde empezar ni cómo ?Leer un artículo relacionado →
Al final de este tutorial, espero poder ayudarte:
- Comprender cómo
v-model
funciona con entradas nativas, centrándose principalmente en radios y casillas de verificación. - Comprender cómo
v-model
funciona en componentes personalizados de forma predeterminada. - Aprenda a crear casillas de verificación y radios personalizadas que emulen
v-model
su funcionamiento de forma nativa.
Nota rápida antes de comenzar : el código ES2015+ se utilizará en todos los ejemplos de código. También preferiré la sintaxis del componente de archivo únicoVue.component
en lugar de usar o new Vue
.
¿Cómo v-modelfunciona normalmente?
La documentación oficial de Vue es bastante buena sobre este tema, pero hay algunos puntos ciegos menores. En cualquier caso, intentaremos cubrirlo bastante a fondo aquí.
En esencia, v-model
es solo una directiva abreviada que nos proporciona un enlace de datos bidireccional, y el código que representa depende del tipo de entrada en el que se utiliza.
Cajas de texto
divinput v-model="message" placeholder="edit me"pMessage: {{ message }}/p!-- OR --pmessage:/pp{{ message }}/ptextarea v-model="message" placeholder="add multiple lines"/textarea/div
Cuando se utiliza un texto input
(incluidos tipos como email
, number
, etc.) o textarea
, v-model="varName"
equivale a :value="varName" @input="e = varName = e.target.value"
. Esto significa que el valor de la entrada se establece varName
después de que cada actualización de la entrada varName
se actualiza al valor de la entrada. Un elemento normal select
también actuará así, aunque una multiple
selección será diferente.
Botones de radio
Entonces, ¿qué pasa con los botones de radio?
divinput type="radio" value="One" v-model="picked"input type="radio" value="Two" v-model="picked"spanPicked: {{ picked }}/span/div
Esto equivale a:
divinput type="radio" value="One" :checked="picked == 'One'" @change="e = picked = e.target.value"input type="radio" value="Two" :checked="picked == 'Two'" @change="e = picked = e.target.value"spanPicked: {{ picked }}/span/div
Fíjate que v-model
ya ni siquiera se toca value
. Sigue haciendo lo mismo en el change
controlador de eventos (aunque se cambió a change
en lugar de input
), pero ahora determina si checked
debe ser verdadero o falso dependiendo de si picked
es el mismo que el valor de ese botón de opción.
Casillas de verificación
Es un poco más difícil hablar de las casillas de verificación porque tienen dos comportamientos diferentes dependiendo de si hay una sola casilla de verificación con un determinado v-model
o varios.
Si está utilizando una sola casilla de verificación, v-model
la tratará como booleana e ignorará el archivo value
.
divinput type="checkbox" value="foo" v-model="isChecked"/div
es lo mismo que…
divinput type="checkbox" value="foo" :checked="!!isChecked" @change="e = isChecked = e.target.checked"/div
Si desea que sea algo distinto a true
and false
, puede usar el atributo true-value
and false-value
, que controla los valores a los que se establecerá su modelo cuando la casilla de verificación esté marcada o no.
divinput type="checkbox" value="foo" v-model="isChecked" true-value="1" false-value="0"/div
es lo mismo que…
divinput type="checkbox" value="foo" :checked="isChecked == '1'" @change="e = isChecked = e.target.checked ? '1' : '0'"/div
Eso es todo para los ejemplos de una sola casilla de verificación. Si tiene varias casillas de verificación que comparten un modelo, entonces esas casillas de verificación llenarán una matriz con los valores de todas las casillas de verificación que están marcadas, pero asegúrese de que el modelo que pasa ya sea una matriz o obtendrá algún comportamiento extraño. Además, los atributos true-value
y false-value
ya no afectan nada.
divtemplate div input type="checkbox" value="foo" v-model="checkedVals" input type="checkbox" value="bar" v-model="checkedVals" input type="checkbox" value="baz" v-model="checkedVals" /div/templatescriptspan export default { data: () = ({ checkedVals: ['bar'] }) }/script/div
El equivalente es un poco más difícil de mantener dentro de la plantilla, por lo que trasladaré parte de la lógica a los métodos del componente:
divtemplate div input type="checkbox" value="foo" v-model="checkedVals" input type="checkbox" value="bar" v-model="checkedVals" input type="checkbox" value="baz" v-model="checkedVals" /div/templatescriptspan export default { data() { return { checkedVals: ['bar'] } }, methods: { shouldBeChecked(val) { return this.checkedVals.includes(val) }, updateVals(e) { let isChecked = e.target.checked let val = e.target.value if (isChecked) { this.checkedVals.push(val) } else { this.checkVals.splice(this.checkedVals.indexOf(val), 1) } } } }/script/div
Esto es mucho más complicado de lo que hemos visto antes, pero si lo analizas, no está tan mal. shouldBeChecked
es true
cuando el valor de esa casilla de verificación está incluido en la matriz y false
si no lo está. updateVals
agrega el valor de la casilla de verificación a la matriz cuando se marca y lo elimina cuando se desmarca.
¿Cómo v-modelfunciona en los componentes?
Dado que Vue no sabe cómo se supone que debe funcionar su componente, o si está tratando de actuar como reemplazo de un determinado tipo de entrada, trata a todos los componentes de la misma manera con respecto a v-model
. En realidad, funciona exactamente de la misma manera que para las entradas de texto, excepto que en el controlador de eventos, no espera que se le pase un objeto de evento, sino que espera que se le pase el valor directamente. Entonces…
divmy-custom-component v-model="myProperty" //div
…es lo mismo que…
divmy-custom-component :value="myProperty" @input="val = myProperty = val" //div
Un componente puede cambiar esto hasta cierto punto usando la model
propiedad:
divexport default { name: 'my-custom-component', model: { prop: 'foo', event: 'bar' }, // ...}/div
v-model
Examinará estas propiedades y, en lugar de usar el value
atributo, usará el atributo que especifique prop
y, en lugar de escuchar el input
evento, usará el evento que especificó event
. Entonces, el my-custom-component
ejemplo anterior en realidad se expandiría a lo siguiente:
divmy-custom-component :foo="myProperty" @bar="val = myProperty = val" //div
Esto es bueno, pero si creamos una radio personalizada o una casilla de verificación, no funciona muy bien. Sin embargo, con algo de trabajo, podemos mover la lógica que v-model
se usa en las radios y las casillas de verificación dentro de nuestros componentes personalizados.
Soporte v-modelen radios personalizadas
En comparación con una casilla de verificación, las radios personalizadas son bastante simples. Aquí hay una radio personalizada muy básica que construí que simplemente envuelve una input
etiqueta y acepta una label
propiedad para agregar el texto de la etiqueta.
divtemplate label input type="radio" :checked="shouldBeChecked" :value="value" @change="updateInput" {{ label }} /label/templatescriptspanexport default { model: { prop: 'modelValue', event: 'change' }, props: { value: { type: spanString, }, modelValue: { default: "" }, label: { type: spanString, required: true }, }, computed: { shouldBeChecked() { return this.modelValue == this.value } } methods: { updateInput() { this.$emit('change', this.value) } }}/script/div
Nota : Solo incluí etiquetas props
que son útiles para explicar cómo deberían funcionar con v-model
, pero input
las etiquetas pueden aprovechar varios otros atributos (como name
o disabled
), así que asegúrese de crear todos los que props
necesitará y pasárselos a input
. También querrás considerar la accesibilidad agregando atributos WAI-ARIA , así como usando espacios para agregar contenido en lugar de accesorios como lo hice aquí con label
.
Quizás pienses que, dado que no lo incluí name
en este ejemplo, un grupo de radios en realidad no se sincronizaría entre sí. En realidad, la actualización del modelo a su vez actualizará los otros botones de opción que comparten ese modelo, por lo que no necesitan compartir un nombre como lo hacen en formularios HTML simples siempre que compartan el mismo modelo.
Soporte v-modelen casillas de verificación personalizadas
Crear casillas de verificación personalizadas es notablemente más complejo que los botones de opción, principalmente porque tenemos que admitir dos casos de uso diferentes: una única casilla de verificación verdadero/falso (que puede usar o no true-value
y/o false-value
) y múltiples casillas de verificación que combinan todos los valores marcados en una matriz.
Entonces, ¿cómo determinamos qué caso de uso es? Podría pensar que necesitamos determinar si hay otras casillas de verificación con el mismo name
atributo, pero en realidad eso no es lo que utiliza el sistema integrado de Vue. Al igual que las radios, Vue no tiene name
en cuenta el atributo en absoluto. Eso solo se usa cuando se envía un formulario de forma nativa. Entonces podrías pensar que lo determina en función de si hay otras casillas de verificación que comparten el mismo modelo, pero tampoco es eso. Está determinado por si el modelo es una matriz o no. Eso es todo.
Por lo tanto, el código se estructurará de manera similar al código del botón de opción personalizado, pero por dentro shouldBeChecked
y updateInput
la lógica se dividirá dependiendo de si modelValue
es o no una matriz.
divtemplate label input type="checkbox" :checked="shouldBeChecked" :value="value" @change="updateInput" {{ label }} /label/templatescriptspanexport default { model: { prop: 'modelValue', event: 'change' }, props: { value: { type: spanString, }, modelValue: { default: false }, label: { type: spanString, required: true }, // We set `true-value` and `false-value` to the default true and false so // we can always use them instead of checking whether or not they are set. // Also can use camelCase here, but hyphen-separating the attribute name // when using the component will still work trueValue: { default: true }, falseValue: { default: false } }, computed: { shouldBeChecked() { if (this.modelValue instanceof spanArray) { return this.modelValue.includes(this.value) } // Note that `true-value` and `false-value` are camelCase in the JS return this.modelValue === this.trueValue } }, methods: { updateInput(event) { let isChecked = event.target.checked if (this.modelValue instanceof spanArray) { let newValue = [...this.modelValue] if (isChecked) { newValue.push(this.value) } else { newValue.splice(newValue.indexOf(this.value), 1) } this.$emit('change', newValue) } else { this.$emit('change', isChecked ? this.trueValue : this.falseValue) } } }}/script/div
Y ahí lo tienes. Sin embargo, puede ser mejor dividir esto en dos componentes diferentes: uno para manejar el cambio único de verdadero/falso y otro para usar en listas de opciones. Eso le permitiría seguir más de cerca el principio de responsabilidad única, pero si está buscando un reemplazo directo para las casillas de verificación, esto es lo que está buscando (más la adición de todos los demás atributos útiles y características personalizadas). podrías querer).
Otros recursos
Hay mucho más que aprender sobre entradas personalizadas, componentes de Vue y Vue en general. Recomiendo echar un vistazo a algunos de estos recursos.
- Conjuntos de componentes de Awesome-Vue
Awesome-Vue es una lista enorme de proyectos y recursos relacionados con Vue, así que siéntete libre de examinar cualquier cosa en esa lista, pero en particular quiero señalar las bibliotecas de UI y los conjuntos de componentes porque prácticamente todos tienen ejemplos de casillas de verificación y radios que puedes ver si te apetece profundizar en su código fuente. - Vue Curated
Esta es una lista similar a Awesome-Vue, pero está más estrictamente seleccionada para que sepas que vale la pena echarle un vistazo a todo lo que aparece en la lista. - Guía de componentes de Vue
La guía oficial de Vue es un excelente lugar para aprender los conceptos básicos sobre todo lo relacionado con Vue. - Documentación de Vue API
Esta documentación es donde profundiza en los detalles realmente profundos de Vue.
Otras lecturas
- Cómo afrontar grandes actualizaciones de herramientas en organizaciones grandes
- SolidStart: una clase diferente de metamarco
- Dar sentido a las funciones de JavaScript "sin sentido"
- Una introducción a la componibilidad de pila completa
(rb, vf, al, il, mrn)Explora más en
- vista
- Marcos
- javascript
Deja un comentario