HB's Thoughts

I try to think really hard.

18.03.2024 г.

Vue emits с параметри

Предаването на събитията във Vue от компонент, обратно към този, който го извиква става с emits. Предаването може да се направи и с Pinia или друга библиотека за управление на 'състоянието', но това ще е за някоя друга статия.

Кое е страница и кое е компонент?

Много често ме питат: 'Този код да го изкарам ли в компонентите или да го оставя в страницата?'. Изградил съм си едно основно правило - ако ми изниква въпроса, дали този код да стане компонент, значи трябва да стане компонент. Самите компоненти също ги групирам по определени х-ки. Много често давам за пример @layer директивата на TailwindCSS, когато някой се чуди как да си групира компонентите.

Компонентите ги разделям на 2 вида:

  1. Спекуланти: Такива, които не изпълняват никаква бизнес логика, а само рисуват и/или трансферират данни;
  2. Работници: Такива, които извършват вторична обработка на входящите параметри и добавят бизнес логика, която връщат към извикващия ги компонент.

Работниците много често може извикват други работници или спекуланти, докато Спекулантите обикновено работят самостоятелно.

Как се предават параметри?

Нека си направим един пример. Имаме компонент (Спекулант), който показва на екрана, колко още страници с продукти остават, преди да се достигне последната страница. Кръщаваме го LoadMore.vue. Входните параметри на компонента са 2: 'кой номер е текущата страница' и 'колко е общия брой страници'. Компонента не извършва никаква бизнес логика. Пресмята колко още страници остават и ги рисува.

// components/utilities/LoadMore.vue

<script lang="ts" setup>
  defineProps<{
    page: number; // текуща страница
    totalPages: number; // общ брой страници
  }>();
</script>

<template>
  <p>{{ `Има още ${totalPages - page} страниц${totalPages - page > 1 ? "и" : "а"}` }}</p>
</template>

Директно предаване на събитие

Нека да преработим малко нашия компонент, така че като се натисне да предава събитието към извикващия компонент, който да пали функция.

// components/utilities/LoadMore.vue

<script lang="ts" setup>
  defineProps<{
    page: number;
    totalPages: number;
  }>();
</script>

<template>
  <div class="col-12 text-center q-mt-md q-mb-xl">
    <button @click="$emit('loadMore', page)"
            :label="`Има още ${totalPages - page} страниц${totalPages - page > 1 ? 'и' : 'а'}`"
            class="btn btn-action btn-action--micro" />
  </div>
</template>

Това е директен начин за предаване на събитието. Когато се натисне бутона, се предава събитието loadMore и параметъра page към извикващия компонент.

// pages/Products.vue

<script lang="ts" setup>
  import LoadMore from '@/components/utilities/LoadMore.vue';
  import { ref } from 'vue';

  const page = ref(1);
  const totalPages = ref(10);

  const loadMore = (page: number) => {
    console.log(`Load more products from page ${page+1}`);
  };
</script>

<template>
  <div>
    <LoadMore :page="page" :totalPages="totalPages" @load-more="loadMore" />
  </div>
</template>

Винаги ползвам kebab-case за емитнатите събитията.

Дефиниране на emit

Когато искате да направите някакво изменение на параметрите, които предавате, преди да емитнете събитието, ще трябва да дефинираите emit-а.

// components/utilities/LoadMore.vue

<script lang="ts" setup>
  defineProps<{
    page: number;
    totalPages: number;
  }>();

  const emit = defineEmits(['loadMore']);

  /**
   * Променяме номера на страницата и емитваме „loadMore“ с актуализирания номер.
   * @param {number} page - Текущият номер на страницата
   */
  const changePage = (page: number) => {
    const nextPage = page + 1;

    emit('loadMore', nextPage);
  };
</script>

<template>
  <div class="col-12 text-center q-mt-md q-mb-xl">
    <button @click="changePage(page)"
            :label="`Има още ${totalPages - page} страниц${totalPages - page > 1 ? 'и' : 'а'}`"
            class="btn btn-action btn-action--micro" />
  </div>
</template>

Така вече имаме много голям контрол върху изходните параметри.

// pages/Products.vue

<script lang="ts" setup>
  import LoadMore from '@/components/utilities/LoadMore.vue';
  import { ref } from 'vue';

  const page = ref(1);
  const totalPages = ref(10);

  const loadMore = (page: number) => {
    console.log(`Load more products from page ${page}`);
  };
</script>

<template>
  <div>
    <LoadMore :page="page" :totalPages="totalPages" @load-more="loadMore" />
  </div>
</template>

Този пример е само показателен. Ако се прави подобно изменението на номера на страницата, то трябва да е логика в извикващия компонент. Но за нашия пример е прекрасен начин да видите, как може да поемете контрола на връщаните данни.


Ако имате мнение или въпроси за статията, не се колебайте да ги споделите.

Влезте в отворената дискусия в GitHub.


Компетентност: Базова
Тагове: Vue