Vue.js para principiantes, lección 10: formularios

Hoy, en la Lección 10 del curso Vue, hablaremos sobre cómo trabajar con formularios. Los formularios le permiten recopilar datos ingresados ​​por el usuario. Además, aquí discutiremos la validación de formularios, es decir, verificar qué se ingresa en ellos.







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-reviewse 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 namealmacenada 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-modelpermite 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-modely vinculemos este campo a una propiedad namede 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-modelcampos input, textareay 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 nullvalor 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-reviewanidado dentro de un componente product, podemos decir que product-reviewes 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-submittedy le pasamos el objeto recién creado productReview.



A continuación, debemos organizar la escucha de este evento colocando lo productsiguiente 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 addReviewcomponente product".



Aquí está el código para este método:



addReview(productReview) {
  this.reviews.push(productReview)
}


Este método toma el objeto productReviewque proviene del método onSubmity luego lo coloca en una matriz reviewsalmacenada 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 productReviewagrega 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 productcolocando el código correspondiente encima del código con el que product-reviewse 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-fory mostramos los datos almacenados en el objeto reviewusando 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-reviewmatriz 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 nameque coloque errorsun mensaje de error en la matriz si el campo está vacío Name required. Otras cadenas que validan los campos reviewy funcionan de manera similar rating. Si alguno de ellos está vacío, arrayse 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, reviewo están submitsin 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, reviewy rating. Si hay datos en todos estos campos, creamos un objeto basado en ellos productReviewy lo enviamos al componente principal. Luego se restablecen las propiedades correspondientes.



Si al menos uno de los campos resulta estar vacío, colocamos errorsun 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 errorspara 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 .numberutilizado en la directiva v-modelpuede 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.





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 .numberle 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 .preventpermite 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






All Articles