image
https://unsplash.com/photos/GJJGpecwvMs

W pierwszym artykule z serii wpisów o Vue, wprowadziłem Cię w podstawowe informacje o komponentach oraz zarządzaniu ich wewnętrznym stanem. Spróbujmy wykorzystać naszą wiedzę do stworzenia re-używalnych komponentów.

Przyjmijmy, że mamy tablicę z informacjami o produktach. Stworzymy prosty komponent, który pozwoli nam wyświetlić sformatowane dane z użyciem pętli v-for. Nasz komponent przyjmie jako props obiekt opisujący dany produkt. Stwórzmy więc, nowy plik z poniższym kodem:

// Product.vue
<template>
  <div class="product">
    <figure class="product-image">
      <img :src="productData.image" alt="Image" />
    </figure>
    <h4 class="product-name">{{ productData.name }}</h4>
    <p class="product-price">{{ productData.price }} zł</p>
    <button @click="addToCart">Dodaj</button>
  </div>
</template>

<script>
  export default {
    name: 'product-item',
    props: {
      productData: Object
    },
    methods: {
      addToCart() {
        this.$emit('add', this.productData.id);
      }
    }
  };
</script>

<style lang="scss">
  .product {
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    padding: 1em;
    margin: 1em;
  }

  .product-image {
    max-width: 100%;
    height: auto;
  }
</style>

Następnie zmieńmy zawartość pliku HelloWorld.vue na poniższą i przeanalizujmy co się dzieje.

<template>
  <div class="products">
    <product
      v-for="product in products"
      :product-data="product"
      :key="product.id"
      @add="onAddToCart"
    />
  </div>
</template>

<script>
  import Product from './Product';

  export default {
    name: 'HelloWorld',
    components: {
      Product
    },
    data() {
      return {
        products: [
          {
            id: 'sdfsdfd',
            name: 'Stół',
            price: 234.3
          },
          {
            id: 'dsfsdfd',
            name: 'Krzesło',
            price: 34.0
          },
          {
            id: 'dsfdfdf',
            name: 'Łózko',
            price: 1234.5
          },
          {
            id: 'swebvb',
            name: 'Lampka',
            price: 4.3
          },
          {
            id: 'dfdf34',
            name: 'Półka',
            price: 23.0
          }
        ]
      };
    },
    methods: {
      onAddToCart(product) {
        console.log('Product id: ', product);
      }
    }
  };
</script>
<style lang="scss">
  .products {
    display: flex;
    flex-wrap: wrap;
  }
</style>

Komponent Product.vue, nie robi nic oprócz wyświetlania naszych danych oraz emitowania eventu na kliknięcie. Emitowanie własnych eventów nie zostało dotychczas omówione, więc teraz dwa słowa o nim.

Własne eventy

Vue dostarcza nam metodę $emit, która jako pierwszy parametr przyjmuje nazwę eventu, pod którą można nasłuchiwać na własne zdarzenie, a jako drugi dane, które zostaną przekazane do funkcji podpiętej do listenera. Emitowany event jest "do złapania" tylko w rodzicu. W tym celu należy w rodzicu poprzedzić nazwę eventu, który będzie nasłuchiwany znakiem @ (która jest skrótem do dyrektywy v-on), a następnie przypisać funkcję, która zostanie wywołana po wyemitowaniu eventu. Należy pamiętać o zachowaniu tej samej nazwy przy emitowaniu i nasłychiwaniu eventu (szczegóły w dokumentacji). Właśnie w taki sposób w pliku HelloWorld.vue została pobrana informacja, który produkt dodać do koszyka (w parametrze eventu przekazuję id produktu). Zauważ też, że dyrektywa v-for została użyta na komponencie, który ma być powtarzany.

Podsumowanie

Przedstawiony przykład pokazuje podstawy kompozycji i tworzenia zależności przy pomocy re-używalnych komponentów. Jest to dobry moment do zatrzymania się na dłuższą chwilę i utrwalenia poznanych funkcjonalosci komponentów Vue. Polecam wykonanie kilku ćwiczeń polegających na komponowaniu widoków z własnych komponenów np. przez utworzenie formularza, w którym zostaną stworzone nowe produkty.