→ Vue.js principiantes lección 1: instancia Vue
→ Vue.js para principiantes, lección 2: atributos de enlace
→ Vue.js principiantes lección 3: renderizado condicional
→ Vue.js principiantes lección 4: listas de renderización
→ Vue .js para principiantes lección 5: procesamiento de eventos
→ Vue.js principiantes lección 6: clases y estilos vinculantes
→ Vue.js principiantes lección 7: propiedades calculadas
→ Vue.js principiantes lección 8: componentes
→ Vue. js para principiantes lección 9: eventos personalizados
El propósito de la lección
Vamos a crear un formulario que permita a los visitantes del sitio enviar reseñas de productos. Al mismo tiempo, es necesario que la reseña se pueda enviar solo si se completan todos los campos del formulario, que deben ser completados.
Código inicial
Esto es lo que hay ahora
index.html
:
<div id="app">
<div class="cart">
<p>Cart({{ cart.length }})</p>
</div>
<product :premium="premium" @add-to-cart="updateCart"></product>
</div>
Se parece a esto
main.js
:
Vue.component('product', {
props: {
premium: {
type: Boolean,
required: true
}
},
template: `
<div class="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p v-if="inStock">In stock</p>
<p v-else>Out of Stock</p>
<p>Shipping: {{ shipping }}</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div
class="color-box"
v-for="(variant, index) in variants"
:key="variant.variantId"
:style="{ backgroundColor: variant.variantColor }"
@mouseover="updateProduct(index)"
></div>
<button
v-on:click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
</div>
</div>
`,
data() {
return {
product: 'Socks',
brand: 'Vue Mastery',
selectedVariant: 0,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: 'green',
variantImage: './assets/vmSocks-green.jpg',
variantQuantity: 10
},
{
variantId: 2235,
variantColor: 'blue',
variantImage: './assets/vmSocks-blue.jpg',
variantQuantity: 0
}
]
}
},
methods: {
addToCart() {
this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId);
},
updateProduct(index) {
this.selectedVariant = index;
console.log(index);
}
},
computed: {
title() {
return this.brand + ' ' + this.product;
},
image() {
return this.variants[this.selectedVariant].variantImage;
},
inStock() {
return this.variants[this.selectedVariant].variantQuantity;
},
shipping() {
if (this.premium) {
return "Free";
} else {
return 2.99
}
}
}
})
var app = new Vue({
el: '#app',
data: {
premium: true,
cart: []
},
methods: {
updateCart(id) {
this.cart.push(id);
}
}
})
Tarea
Necesitamos que los visitantes del sitio puedan dejar comentarios sobre los productos, pero nuestro sitio aún no tiene los medios para recibir datos de los usuarios. Las formas son esos medios.
La solucion del problema
Para resolver la tarea que tenemos ante nosotros, necesitamos crear un formulario. Comencemos creando un nuevo componente específicamente para trabajar con un formulario. Llamemos a este componente
product-review
. Se eligió este nombre porque el componente admitirá el formulario para recopilar reseñas de productos. El componente product-review
se anidará dentro del componente product
.
Registremos un nuevo componente, comencemos a formar su plantilla y equipamos con algunos datos:
Vue.component('product-review', {
template: `
<input>
`,
data() {
return {
name: null
}
}
})
Como puede ver, hay un elemento en la plantilla del componente
<input>
y hay una propiedad en los datos del componente data
, mientras está vacío.
¿Cómo vinculo lo que el usuario ingresa en un campo a una propiedad
name
?
En las lecciones anteriores, hablamos sobre la vinculación de datos mediante directivas
v-bind
, pero luego solo consideramos la vinculación unidireccional. El flujo de datos pasó de la propiedad que almacena los datos al control que los procesa. Y ahora necesitamos que lo que el usuario ingrese en el campo esté en la propiedad name
almacenada en los datos del componente. En otras palabras, queremos que el flujo de datos se dirija desde el campo de entrada a la propiedad.
Directiva modelo V
La directiva
v-model
permite organizar el enlace de datos bidireccional. Con este esquema de trabajo, si aparece algo nuevo en el campo de entrada, esto conduce a un cambio en los datos. Y, en consecuencia, cuando los datos cambian, se actualiza el estado del control que usa estos datos.
Agreguemos una directiva al campo de entrada
v-model
y vinculemos este campo a una propiedad name
de los datos del componente.
<input v-model="name">
Ahora agreguemos el código de formulario completo a la plantilla del componente:
<form class="review-form" @submit.prevent="onSubmit">
<p>
<label for="name">Name:</label>
<input id="name" v-model="name" placeholder="name">
</p>
<p>
<label for="review">Review:</label>
<textarea id="review" v-model="review"></textarea>
</p>
<p>
<label for="rating">Rating:</label>
<select id="rating" v-model.number="rating">
<option>5</option>
<option>4</option>
<option>3</option>
<option>2</option>
<option>1</option>
</select>
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
Como se puede ver, los
v-model
campos input
, textarea
y están equipadas con la Directiva select
. Tenga en cuenta que al configurar el campo select
, se utilizó un modificador .number
(hablaremos de ello con más detalle a continuación). Esto le permite convertir los datos correspondientes a un tipo Number
, mientras que generalmente se representa en una cadena.
Complementemos el conjunto de datos del componente añadiéndole los datos a los que están vinculados los controles descritos anteriormente:
data() {
return {
name: null,
review: null,
rating: null
}
}
En la parte superior de la plantilla de formulario, puede ver que cuando se envía el formulario, se llama a un método
onSubmit
. Pronto crearemos este método. Pero primero, hablemos del papel de la construcción .prevent
.
Este es un modificador de eventos. Evita que la página se vuelva a cargar después de que se genere el evento
submit
. También hay otros modificadores de eventos útiles . Es cierto que no hablaremos de ellos.
Ahora estamos listos para crear un método
onSubmit
. Comencemos con este código:
onSubmit() {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating
}
this.name = null
this.review = null
this.rating = null
}
Como puede ver, este método crea un objeto basado en los datos ingresados por el usuario. Se escribe una referencia a él en una variable
productReview
. Aquí dejamos caer en el null
valor de las propiedades name
, review
, rating
. Pero el trabajo aún no ha terminado. Todavía necesitamos enviar a algún lugar productReview
. ¿Dónde enviar este objeto?
Tiene sentido almacenar las reseñas de productos en el mismo lugar donde se almacenan los datos de los componentes
product
. Dado que el componente está product-review
anidado dentro de un componente product
, podemos decir que product-review
es un hijo del componente product
. Como aprendimos en la lección anterior, puede usar eventos generados usando para enviar datos de componentes secundarios a componentes principales $emit
.
Refinemos el método
onSubmit
:
onSubmit() {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating
}
this.$emit('review-submitted', productReview)
this.name = null
this.review = null
this.rating = null
}
Ahora generamos un evento con un nombre
review-submitted
y le pasamos el objeto recién creado productReview
.
A continuación, debemos organizar la escucha de este evento colocando lo
product
siguiente en la plantilla :
<product-review @review-submitted="addReview"></product-review>
Esta línea dice así: "Cuando ocurre un evento
review-submitted
, es necesario ejecutar el método del addReview
componente product
".
Aquí está el código para este método:
addReview(productReview) {
this.reviews.push(productReview)
}
Este método toma el objeto
productReview
que proviene del método onSubmit
y luego lo coloca en una matriz reviews
almacenada en los datos del componente product
. Pero aún no existe tal matriz en los datos de este componente. Así que agregémoslo allí:
reviews: []
¡Maravilloso! Los elementos del formulario ahora están vinculados a los datos del componente
product-review
. Estos datos se utilizan para crear el objeto productReview
. Y este objeto se pasa, cuando se envía el formulario, al componente product
. Como resultado, el objeto se productReview
agrega a la matriz reviews
, que se almacena en los datos del componente product
.
Visualización de reseñas de productos
Ahora todo lo que queda es mostrar en la página del producto las reseñas dejadas por los visitantes del sitio. Haremos esto en el componente
product
colocando el código correspondiente encima del código con el que product-review
se coloca el componente en el componente product
.
<div>
<h2>Reviews</h2>
<p v-if="!reviews.length">There are no reviews yet.</p>
<ul>
<li v-for="review in reviews">
<p>{{ review.name }}</p>
<p>Rating: {{ review.rating }}</p>
<p>{{ review.review }}</p>
</li>
</ul>
</div>
Aquí creamos una lista de revisiones usando la directiva
v-for
y mostramos los datos almacenados en el objeto review
usando notación de puntos.
En la etiqueta,
<p>
comprobamos si hay algo en la matriz reviews
(comprobando su longitud). Si no hay nada en la matriz, imprimimos un mensaje There are no reviews yet
.
Página del formulario de comentarios
Validación de formularios
Los formularios suelen contener campos que deben completarse antes de enviar el formulario. Por ejemplo, no queremos que los usuarios envíen reseñas en las que el campo de texto de la reseña esté vacío.
Afortunadamente para nosotros, HTML5 es compatible con
required
. Su uso se ve así:
<input required >
Tal construcción conducirá a la visualización automática de un mensaje de error si el usuario intenta enviar un formulario en el que el campo requerido está vacío.
Mensaje de error
Tener validadores de campo de formulario estándar en el navegador es muy bueno, ya que nos puede liberar de crear nuestros propios validadores de campo. Pero la forma en que se lleva a cabo la verificación de datos estándar puede, en algún caso especial, no convenirnos. En esta situación, tiene sentido escribir su propio código de validación de formulario.
Validación de formularios personalizados
Hablemos sobre cómo crear su propio sistema de validación de formularios.
Incluyamos una
product-review
matriz para almacenar mensajes de error en los datos del componente . Llamémoslo errors
:
data() {
return {
name: null,
review: null,
rating: null,
errors: []
}
}
Nos gustaría agregar a esta matriz información sobre los errores que ocurren en situaciones donde los campos de formulario están vacíos. Estamos hablando del siguiente código:
if(!this.name) this.errors.push("Name required.")
if(!this.review) this.errors.push("Review required.")
if(!this.rating) this.errors.push("Rating required.")
La primera de estas líneas le dice al sistema
name
que coloque errors
un mensaje de error en la matriz si el campo está vacío Name required
. Otras cadenas que validan los campos review
y funcionan de manera similar rating
. Si alguno de ellos está vacío, array
se enviará un mensaje de error a la matriz .
¿Dónde poner este código?
Como queremos que los mensajes de error que se escriben en la matriz sólo cuando, al tratar de enviar el formulario, resulta que los campos son
name
, review
o están submit
sin rellenar, podemos colocar este código en el método onSubmit
. Además, reescribiremos el código que ya está en él, añadiéndole algunas comprobaciones:
onSubmit() {
if(this.name && this.review && this.rating) {
let productReview = {
name: this.name,
review: this.review,
rating: this.rating
}
this.$emit('review-submitted', productReview)
this.name = null
this.review = null
this.rating = null
} else {
if(!this.name) this.errors.push("Name required.")
if(!this.review) this.errors.push("Review required.")
if(!this.rating) this.errors.push("Rating required.")
}
}
Ahora, comprobamos los campos
name
, review
y rating
. Si hay datos en todos estos campos, creamos un objeto basado en ellos productReview
y lo enviamos al componente principal. Luego se restablecen las propiedades correspondientes.
Si al menos uno de los campos resulta estar vacío, colocamos
errors
un mensaje de error en la matriz , dependiendo de lo que el usuario no ingresó antes de enviar el formulario.
Todo lo que queda es mostrar estos errores, lo que se puede hacer con el siguiente código:
<p v-if="errors.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="error in errors">{{ error }}</li>
</ul>
</p>
Aquí se usa una directiva
v-if
, con la cual verificamos la matriz errors
para detectar la presencia de mensajes de error en ella (de hecho, analizamos la longitud de la matriz). Si hay algo en la matriz, se muestra un elemento <p>
que, cuando se aplica v-for
, muestra una lista de errores de la matriz errors
.
Mensajes de error
Ahora tenemos nuestro propio sistema de validación de formularios.
Usando el modificador .number
El modificador
.number
utilizado en la directiva v-model
puede resultar muy útil. Pero al aplicarlo, tenga en cuenta que hay un problema asociado. Si el valor correspondiente está vacío, se representará como una cadena, no como un número. El libro de recetas de Vue ofrece una solución a este problema. Consiste en convertir explícitamente el valor correspondiente a un tipo numérico:
Number(this.myNumber)
Taller
Agregue la siguiente pregunta al formulario: "¿Recomendaría este producto?" Hágalo de manera que el usuario pueda responder usando los botones de opción "sí" y "no". Verifique la respuesta a esta pregunta e incluya los datos relevantes en el objeto
productReview
.
- Aquí hay una plantilla que puede usar para resolver este problema.
- Aquí está la solución al problema.
Salir
Hoy hablamos de trabajar con formularios. Esto es lo más importante que ha aprendido hoy:
- Puede utilizar la directiva para organizar el enlace de datos bidireccional a los elementos del formulario
v-model
. - El modificador
.number
le dice a Vue que lance el valor correspondiente a un tipo numérico. Pero al trabajar con él, hay un problema relacionado con el hecho de que los valores vacíos siguen siendo cadenas. - El modificador de eventos le
.prevent
permite evitar la recarga de la página después de que se envía el formulario. - Con Vue, puede implementar un mecanismo bastante simple para la validación de formularios personalizados.
¿Hiciste tu tarea hoy?
→ Vue.js principiantes lección 1: instancia Vue
→ Vue.js para principiantes, lección 2: atributos de enlace
→ Vue.js principiantes lección 3: renderizado condicional
→ Vue.js principiantes lección 4: listas de renderización
→ Vue .js para principiantes lección 5: procesamiento de eventos
→ Vue.js principiantes lección 6: clases y estilos vinculantes
→ Vue.js principiantes lección 7: propiedades calculadas
→ Vue.js principiantes lección 8: componentes
→ Vue. js para principiantes lección 9: eventos personalizados