Vue.js para iniciantes, lição 10: formulários

Hoje, na Lição 10 do curso Vue, falaremos sobre como trabalhar com formulários. Os formulários permitem que você colete os dados inseridos pelo usuário. Além disso, discutiremos aqui a validação dos formulários, ou seja, a validação do que está inserido neles.







Vue.js para iniciantes lição 1: instância Vue

Vue.js para iniciantes, lição 2: atributos de vinculação

Vue.js para iniciantes lição 3: renderização condicional

Vue.js para iniciantes lição 4: exibição de listas

Vue .js para iniciantes lição 5: processamento de eventos

Vue.js para iniciantes lição 6: ligando classes e estilos

Vue.js para iniciantes lição 7: propriedades calculadas

Vue.js para iniciantes lição 8: componentes

Vue. js para iniciantes lição 9: eventos personalizados



O propósito da lição



Vamos criar um formulário que permite aos visitantes do site enviar análises de produtos. Nesse caso, é necessário que o envio da resenha só seja possível se todos os campos do formulário estiverem preenchidos, os quais devem ser preenchidos.



Código inicial



Aqui está o que está acontecendo agora index.html:



<div id="app">
  <div class="cart">
    <p>Cart({{ cart.length }})</p>
  </div>

  <product :premium="premium" @add-to-cart="updateCart"></product>
</div>


É assim 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);
    }
  }
})


Tarefa



Precisamos que os visitantes do site possam deixar comentários sobre os produtos, mas nosso site ainda não possui meios para receber dados dos usuários. As formas são esses meios.



A solução do problema



Para resolver a tarefa diante de nós, precisamos criar um formulário. Vamos começar criando um novo componente especificamente para trabalhar com um formulário. Vamos chamar esse componente product-review. Este nome foi escolhido porque o componente proporcionará o funcionamento do formulário destinado à coleta de avaliações de produtos. O componente product-reviewserá aninhado dentro do componente product.



Vamos registrar um novo componente, começar a formar seu template e equipá-lo com alguns dados:



Vue.component('product-review', {
  template: `
    <input>
  `,
  data() {
    return {
      name: null
    }
  }
})


Como você pode ver, há um elemento no modelo do componente <input>e uma propriedade nos dados do componente data, embora esteja vazio.



Como vinculo o que o usuário insere em um campo a uma propriedade name?



Nas lições anteriores, falamos sobre vinculação de dados usando diretivas v-bind, mas consideramos apenas a vinculação unilateral. O fluxo de dados foi da propriedade que armazena os dados para o controle que os renderiza. E agora precisamos que o que o usuário insere no campo seja na propriedade namearmazenada nos dados do componente. Em outras palavras, queremos que o fluxo de dados seja direcionado do campo de entrada para a propriedade.



Diretiva modelo V



A diretiva v-modelpermite organizar a vinculação de dados bidirecional. Com este esquema de trabalho, se algo novo aparecer no campo de entrada, isso leva a uma mudança nos dados. E, portanto, quando os dados são alterados, o estado do controle que usa esses dados é atualizado.



Vamos adicionar uma diretiva ao campo de entrada v-modele vincular esse campo a uma propriedade namedos dados do componente.



<input v-model="name">


Agora vamos adicionar o código completo do formulário ao modelo do 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 você pode ver, os v-modelcampos input, textareae estão equipados com a diretiva select. Observe que ao configurar o campo select, um modificador foi usado .number(falaremos sobre isso em mais detalhes abaixo). Isso permite que você converta os dados correspondentes em um tipo Number, embora geralmente sejam representados em uma string.



Vamos suplementar o conjunto de dados do componente adicionando a ele os dados aos quais os controles descritos acima estão vinculados:



data() {
  return {
    name: null,
    review: null,
    rating: null
  }
}


Na parte superior do modelo de formulário, você pode ver que, quando o formulário é enviado, um método é chamado onSubmit. Estaremos criando este método em breve. Mas primeiro, vamos falar sobre o papel da construção .prevent.



Este é um modificador de evento. Impede que a página seja recarregada após a ocorrência de um evento submit. Existem também outros modificadores de eventos úteis . É verdade, não vamos falar sobre eles.



Agora estamos prontos para criar um método onSubmit. Vamos começar com 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 você pode ver, este método cria um objeto com base nos dados inseridos pelo usuário. Uma referência a ele é gravada em uma variável productReview. Aqui nós cair em nullvalores de propriedade name, review, rating. Mas o trabalho ainda não acabou. Ainda precisamos enviar para algum lugar productReview. Para onde enviar este objeto?



Faz sentido armazenar análises de produtos no mesmo local onde os dados dos componentes são armazenados product. Dado que o componente está product-reviewaninhado dentro do componente product, podemos dizer que ele product-reviewé filho do componente product. Como aprendemos na lição anterior, você pode usar eventos gerados para enviar dados de componentes filhos para componentes pais $emit.



Vamos refinar o 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
}


Agora geramos um evento com um nome review-submittede passamos o objeto recém-criado nele productReview.



Em seguida, precisamos organizar a escuta para este evento, colocando o productseguinte no modelo :



<product-review @review-submitted="addReview"></product-review>


Esta linha é lida assim: "Quando ocorre um evento review-submitted, o método do addReviewcomponente precisa ser executado product."



Aqui está o código para este método:



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


Esse método pega o objeto productReviewque veio do método onSubmite o coloca em uma matriz reviewsarmazenada nos dados do componente product. Mas ainda não existe tal array nos dados deste componente. Então, vamos adicioná-lo lá:



reviews: []


Maravilhoso! Os elementos do formulário agora estão vinculados aos dados do componente product-review. Esses dados são usados ​​para criar o objeto productReview. E esse objeto é passado, quando o formulário é enviado, para o componente product. Como resultado, o objeto é productReviewadicionado ao array reviews, que é armazenado nos dados do componente product.



Exibindo análises de produtos



Agora, resta exibir na página do produto as avaliações feitas pelos visitantes do site. Faremos isso no componente product, colocando o código correspondente acima do código com o qual o componente product-reviewé colocado no 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>


Aqui, criamos uma lista de revisões usando a diretiva v-fore exibimos os dados armazenados no objeto reviewusando a notação de ponto. 



Na tag, <p>verificamos se há algo no array reviews(verificando seu comprimento). Se não houver nada no array, imprimimos uma mensagem There are no reviews yet.





Página de formulário de feedback



Validação de formulário



Os formulários geralmente contêm campos que devem ser preenchidos antes do envio do formulário. Por exemplo, não queremos que os usuários enviem resenhas em que o campo de texto da resenha esteja vazio.



Felizmente para nós, o HTML5 suporta o required. Seu uso é parecido com este:



<input required >


Tal construção levará a uma exibição automática de uma mensagem de erro se o usuário tentar enviar um formulário no qual o campo obrigatório está vazio.





Mensagem de erro



Ter validadores de campo de formulário padrão no navegador é muito bom, pois pode nos livrar de criar nossos próprios validadores de campo. Mas a forma como a verificação de dados padrão é realizada pode, em alguns casos especiais, não nos servir. Nessa situação, faz sentido escrever seu próprio código de validação de formulário.



Validação de formulário personalizado



Vamos falar sobre como criar seu próprio sistema de validação de formulário.



Vamos incluir uma product-reviewmatriz para armazenar mensagens de erro nos dados do componente . Vamos chamá-lo errors:



data() {
  return {
    name: null,
    review: null,
    rating: null,
    errors: []
  }
}


Gostaríamos de adicionar a este array informações sobre erros que ocorrem em situações onde os campos do formulário estão vazios. Estamos falando sobre o seguinte 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.")


A primeira dessas linhas diz ao sistema namepara colocar errorsuma mensagem de erro no array se o campo estiver vazio Name required. Outras strings que validam os campos reviewe funcionam de maneira semelhante rating. Se algum deles estiver vazio, arrayuma mensagem de erro será enviada ao array .



Onde colocar esse código?



Desde que nós queremos as mensagens de erro a serem gravados para a matriz somente quando, ao tentar enviar o formulário, verifica-se que os campos são name, reviewou estão submitpreenchido, podemos colocar esse código no método onSubmit. Além disso, iremos reescrever o código que já está nele, adicionando algumas verificações a ele:



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.")
  }
}


Agora vamos verificar os campos name, reviewe rating. Se houver dados em todos esses campos, criamos um objeto com base neles productReviewe o enviamos para o componente pai. Em seguida, as propriedades correspondentes são redefinidas.



Se pelo menos um dos campos estiver vazio, colocamos errorsuma mensagem de erro no array , dependendo do que o usuário não inseriu antes de enviar o formulário.



Resta exibir esses erros, o que pode ser feito com o seguinte 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>


Aqui, uma diretiva é usada v-ifcom a qual verificamos o array errorsquanto à presença de mensagens de erro nele (na verdade, analisamos o comprimento do array). Se houver algo na matriz, é exibido um elemento <p>que, quando aplicado v-for, exibe uma lista de erros da matriz errors.





Mensagens de erro



Agora temos nosso próprio sistema de validação de formulário.



Usando o modificador .number



O modificador .numberusado na diretiva v-modelpode ser muito útil. Mas, ao usá-lo, lembre-se de que há um problema com ele. Se o valor correspondente estiver vazio, ele será representado como uma string, não um número. O Livro de Receitas Vue oferece uma solução para este problema. Consiste em converter explicitamente o valor correspondente em um tipo numérico:



Number(this.myNumber)


Oficina



Adicione a seguinte pergunta ao formulário: "Você recomendaria este produto?" Faça com que o usuário possa responder usando os botões de opção sim e não. Verifique a resposta a esta pergunta e inclua os dados relevantes no objeto productReview.





Resultado



Hoje falamos sobre como trabalhar com formulários. Aqui está a coisa mais importante que você aprendeu hoje:



  • Você pode usar a diretiva para organizar a vinculação de dados bidirecional para os elementos do formulário v-model.
  • O modificador .numberdiz a Vue para converter o valor correspondente em um tipo numérico. Mas ao trabalhar com isso, há um problema relacionado ao fato de que os valores vazios permanecem strings.
  • O modificador de evento .preventpermite evitar que a página seja recarregada após o envio do formulário.
  • Com o Vue, você pode implementar um mecanismo bastante simples para validação de formulário personalizado.


Você fez sua lição de casa hoje?



Vue.js para iniciantes lição 1: instância Vue

Vue.js para iniciantes, lição 2: atributos de vinculação

Vue.js para iniciantes lição 3: renderização condicional

Vue.js para iniciantes lição 4: exibição de listas

Vue .js para iniciantes lição 5: processamento de eventos

Vue.js para iniciantes lição 6: ligando classes e estilos

Vue.js para iniciantes lição 7: propriedades calculadas

Vue.js para iniciantes lição 8: componentes

Vue. js para iniciantes lição 9: eventos personalizados






All Articles