Vue.js para principiantes lección 9: eventos personalizados

En la lección anterior de nuestro curso de Vue, aprendió cómo crear componentes y cómo pasar datos de las entidades principales a los niños mediante el mecanismo de accesorios. ¿Qué pasa si los datos deben transferirse en la dirección opuesta? Hoy, en la novena lección, aprenderá cómo establecer una comunicación bidireccional entre componentes de diferentes niveles.







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



Queremos que el componente productpueda notificar a la entidad principal, la instancia raíz de Vue, que ha ocurrido un evento. En este caso, productdeberá enviar, junto con la notificación de la ocurrencia del evento, algunos datos.



Código inicial



El archivo de index.htmlproyecto de muestra ahora contiene el siguiente código:



<div id="app">
  <product :premium="premium"></product>
</div>


Aquí está el contenido del archivo 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 class="cart">
        <p>Cart({{ cart }})</p>
      </div>
    </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
        }
      ],
      cart: 0,
    }
  },
    methods: {
      addToCart() {
        this.cart += 1;
      },
      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
  }
})


Tarea



Ahora que se productpresenta como un componente independiente, no producttiene sentido que el código relacionado con el carrito esté en él. Si cada producto tiene su propia canasta de la que necesitamos monitorear el estado, nuestra aplicación se convertirá en un gran desastre. En cambio, nos gustaría que el carrito existiera en la raíz de la instancia de Vue. También necesitamos el componente para productinformar a la instancia raíz de Vue sobre cómo agregar artículos al carrito, es decir, sobre los clics en el botón Add to cart.



Decisión



Muevamos los datos relacionados con el carrito de regreso a la instancia raíz de Vue:



var app = new Vue({
  el: '#app',
  data: {
    premium: true,
    cart: 0
  }
})


A continuación, volvamos a mover la plantilla del carrito index.html, llevando su código a este formulario:



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

  <product :premium="premium"></product>
</div>


Ahora, si abre la página de la aplicación en un navegador y hace clic en el botón Add to cart, no sucede nada como se esperaba.





Hacer clic en el botón Agregar al carrito no conduce a nada todavía



¿Qué debería suceder cuando se hace clic en este botón? Necesitamos que al hacer clic en él, la instancia raíz de Vue reciba una notificación que llame a un método que actualice el carrito, es decir, actualice el valor que está almacenadocart.



Para lograr esto, primero reescribamos el código del método deladdToCartcomponenteproduct.



Ahora se ve así:



addToCart() {
  this.cart += 1;
},


Llevémoslo a este formulario:



addToCart() {
  this.$emit('add-to-cart');
},


¿Qué significa todo esto?



Entonces eso es lo que es. Cuando se llama al método addToCart, se genera un evento con nombre personalizado add-to-cart. En otras palabras, cuando Add to cartse hace clic en un botón , se llama a un método que genera un evento que indica que el botón se acaba de presionar (es decir, que el evento desencadenado por el clic del botón acaba de ocurrir).



Pero en este momento, nada en la aplicación espera este evento, no lo está escuchando. Agreguemos un detector de eventos a index.html:



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


Aquí usamos la construcción de vista de la @add-to-cardmisma manera que usamos la construcción :premium. Pero si se :premiumtrata de una "canalización" a través de la cual se pueden transmitir datos al componente secundario desde el principal, entonces @add-to-cartse puede comparar con el "receptor de radio" del componente principal, que recibe información del componente secundario de que se ha presionado un botón Add to cart. Dado que la "radio" está en una etiqueta <product>anidada <div id="app">, esto significa que cuando llega información sobre un clic , Add to cartse llamará updateCartal método ubicado en la instancia raíz de Vue. Traducido al lenguaje ordinario, el



código se @add-to-cart=«updateCart»ve así: "Cuando escuche que ha ocurrido un evento add-to-cart, llame al método updateCart".



Este método, que ahora se declarará en el objeto de opciones utilizado al crear una instancia de Vue, probablemente lo haya visto en alguna parte:



methods: {
  updateCart() {
    this.cart += 1;
  }
}


De hecho, este es exactamente el mismo método que se utilizó anteriormente product. Pero ahora está en la instancia raíz de Vue y se llama al hacer clic en un botón Add to cart.





El botón funciona de nuevo



Cuando hace clic en un botón ubicado en un componenteproduct, se llama a un métodoaddToCartque genera un evento. La instancia raíz de Vue, escuchando la radio, se entera de que el evento ha ocurrido y llama a un métodoupdateCartque incrementa el número almacenado encart.



Logramos nuestro objetivo, pero en una aplicación real, saber que ha ocurrido un evento, que se ha añadido un determinado producto al carrito, no traerá mucho beneficio. En realidad, necesita saber al menos qué producto se agregó al carrito. Esto significa que en el caso de que se genere en respuesta a presionar el botón, también es necesario transferir algunos datos.



Los datos pasados ​​en el evento se pueden describir como el segundo argumento pasado$emiten el código del método deladdToCartcomponente.product:



this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId);


Ahora el evento pasa el identificador ( variantId) del producto que el usuario quiere agregar al carrito. Esto significa que, en lugar de simplemente aumentar la cantidad de artículos en el carrito, podemos ir más allá y almacenar información más detallada sobre los artículos agregados en el carrito. Para hacer esto, primero convertimos la canasta en una matriz escribiendo una matriz vacía en cart:



cart: []


A continuación, reescribamos el método updateCart. En primer lugar, ahora aceptará idel mismo identificador de producto que ahora se pasa en el evento, y en segundo lugar, ahora pondrá lo que recibió en una matriz:



methods: {
  updateCart(id) {
    this.cart.push(id);
  }
}


Después de un solo clic en el botón, el identificador de producto se agrega a la matriz. La matriz se muestra en la página.





La matriz con el ID del producto se muestra en la página



. No es necesario mostrar la matriz completa en la página. Estamos satisfechos con la salida de la cantidad de productos agregados al carrito, es decir, a la matrizcart. Por lo tanto, podemos reescribir el código de la etiqueta<p>, que muestra información sobre la cantidad de productos agregados al carrito, de la siguiente manera:



<p>Cart({{ cart.length }})</p>




La página muestra información sobre la cantidad de artículos agregados al carrito.



Ahora simplemente mostramos la longitud de la matriz en la página o, en otras palabras, la cantidad de artículos en el carrito. Externamente, el carrito se ve igual que antes, pero ahora, en lugar de simplemente aumentar el valor de una propiedad numéricacart, almacenamos en una matrizcartinformación sobre qué artículo se agregó al carrito.



Taller



Agregue un botón al proyecto que elimine el cartproducto agregado allí anteriormente de la matriz . Al hacer clic en este botón, se debe generar un evento que contenga información sobre el identificador del producto que se eliminará del carrito.





Salir



Esto es lo que aprendiste hoy:



  • Un componente puede informar a la entidad padre que algo ha sucedido en él usando la construcción $emit.
  • Un componente principal puede utilizar un controlador de eventos especificado mediante la directiva v-on(o su versión abreviada @) para organizar una respuesta a los eventos generados por los componentes secundarios. Si ocurre un evento, se puede llamar a un controlador de eventos en el componente principal.
  • , , .


Si está estudiando el curso y ha llegado a esta lección, cuéntenos sobre el propósito por el que está haciendo, lo que quiere lograr al dominar Vue.



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