Event Bus – Riešime Vue.js #3


Event Bus – Riešime Vue.js #3

Aj na tej najmenej interaktívnej webovej stránke musíš určitým spôsobom spracovávať sadu eventov a vykonať na nej určité akcie. V časoch jQuery bolo toto spracovanie eventov veľmi jednoduché. Stačilo vedieť napísať správny selector, názov eventu, s ktorým budem pracovať a definovať callback, ktorý sa vykoná, keď event nastane. Kód som mohol vložiť kdekoľvek, a aj kontext, na ktorý boli eventy aplikované, bol definovaný len zadaným selektorom. Tento prístup mal svoje klady, ale aj zápory. A to nehovorím o problémoch s dynamickým obsahom, kedy bolo treba premýšľať, ktorú „bind“ funkciu použiť. Ako ale na eventy vo Vue.js?

Vue.js eventy

Vo Vue.js, bez nejakého „hackovania“, pracuješ s eventami v rámci daného komponentu, prípadne môžeš posúvať tieto eventy vyššie, po jeho parentoch. K dispozícii máš všetky štandardné eventy, s ktorými si pracoval aj v jQuery. Jediná zmena je, že „bind“ vytváraš ako property v HTML elemente. Zápis je veľmi jednoduchý:

<template>
  <button v-on:click="showAutocomplete">Zobraziť vyhľadávanie</button>
 
  <form method="get" accept-charset="utf-8" :action="actionUrl" v-on:submit.prevent="search" v-if="isVisible">
    <input type="text" name="search" v-on:input="autocomplete" />
  </form>
</template>
 
<script>
export default {
  name: "Form",
  props: ["actionUrl"],
  data: function() {
     return {
        isVisible: false,
        keyword: ''
     }
  },
  methods: {
    search: function() {
      window.location.href = '/vyhladavanie?search=' + encodeURIComponent(this.keyword);
    },
    autocomplete: function(event) {
      this.keyword = event.currentTarget.value;
      this.$emit("autocomplete", this.keyword);
    },
    showAutocomplete: function() {
       this.isVisible = true;
    }
  }
};
</script>
<template>
  <div class="autocomplete">
    <autocomplete-form :actionUrl="actionUrl" v-on:autocomplete="autocompleteParent" />
  </div>
</template>
 
<script>
// Parent
import FormComponent from "./Form";
 
export default {
  name: "Autocomplete",
  components: {
    "autocomplete-form": FormComponent
  },
  methods: {
     autocompleteParent: function() {
        // do autocomplete stuff
     }
  }
};
</script>

Na príklade som odchytil hneď niekoľko eventov – odoslanie formulára, kliknutie na button a písanie do inputu. Komponent v bloku methods obsahuje callbacky na tieto eventy. Namiesto v-on môžem písať skratku @. Odporúčam vybrať si jeden spôsob zápisu a ostať konzistentný. Podchytiť konzistentnosť vieš vďaka ESLint, konkrétne pravidlom vue/v-on-style.

Pri submit evente možno nevieš, čo chce byť .prevent. Ide o takzvaný Event modifier, ktorým vieš napríklad ovplyvniť defaultné správanie daného eventu. V tomto prípade poviem browseru, aby zamedzil defaultnému odoslaniu formulára. V štandardnom JS poňatí ide o vykonanie príkazu event.preventDefault(). Viac sa o Event modifiers dočítaš v oficiálnej dokumentácii.

Funkciou this.$emit(…) som vyvolal custom event, na ktorý potom viem počúvať ako na klasický event. Táto metóda je vhodná, hlavne keď potrebujem posunúť eventy mimo komponentu, k parentom.

Event Bus

Predstav si teraz, že máš veľkú aplikáciu a potrebuješ zabezpečiť komunikáciu medzi rôznymi komponentmi. Môžeš si napríklad posúvať dáta pomocou properties. Čo ak mi to štruktúra komponentov takto riešiť neumožňuje? A navyše, posúvanie properties do x úrovní už začína byť celkom neprehľadné. Ešte väčší deal by bolo riešiť bublanie eventov smerom nahor. Alebo si zober iný prípad – implementovanie Google Tag Managera. Tam musíš zachytiť celú sadu eventov a mať dostupné aj dáta na entity, ktoré spolu ani v rámci komponentov nesúvisia. Ako napríklad pridanie produktu do košíka a odoslanie objednávky. V oboch prípadoch potrebujem vyvolať viacero callbackov, a to nielen kvôli GTM. Ak by som celý kód mal písať v danom komponente, čoskoro by sa stal neprehľadným, ako tomu bolo v jQuery fáze.

Vo Vue.js sa na riešenie tejto problematiky používa jednoduchý globálny Event Bus. Ide o globálnu inštanciu Vue.js, v ktorej môžem s eventami veľmi ľahko pracovať.

<script>
import Vue from 'vue';
 
const eventBus = new Vue()
 
// register an event handler
eventBus.$on("onAppStarted", () => console.log("App Started!"))
 
// publish an event
eventBus.$emit("onAppStarted")
</script>

V mojej Nuxt aplikácii vytvorím Event Bus ako nový plugin, ktorý potom injectnem do Nuxt kontextu.

<script>
// ~/nuxt.config.js
 
module.exports = {
    /*
     ** Plugins to load before mounting the App
     */
    plugins: [
        '~/plugins/eventBus.js'
    ]
}
 
// ~/plugins/eventBus.js
import Vue from 'vue';
 
const eventBus = new Vue();
 
export default ({ app }, inject) => {
    inject('eventBus', eventBus);
}
</script>

Teraz môžem Event Bus používať v aplikácii nielen vo Vue komponentoch, ale napríklad aj vo Vuex store. Tento prístup nie je odporúčané využívať úplne na všetky eventy, ale len na tie, ktoré skutočne potrebuješ mať dostupné globálne.

Na príklade si mohol vidieť aj to, aké jednoduché je rozširovať funkcionalitu Nuxt aplikácie pomocou pluginov. Toto bol, samozrejme, jednoduchý prípad, ale v ďalších častiach ti ukážem aj komplexnejšie veci. Napríklad, ako na preklady, ako na Monorepo štruktúru pomocou Yarn workspaces, alebo aj ako spustiť Typescript podporu. Preto nezabudni sledovať našu FB stránku, aby ti nič neuniklo.

Ak máš záujem riešiť Vue.js aj ty, prečítaj si naše predošlé články zo série:

Nuxt aplikácia v kontajneroch

Riešime Vue.js #1

Testovanie Vue.js komponentov

vue.js
axios
moxios
testing

V prípade, že by si nám chcel pomôcť s vývojom ďalšej generácie nášho projektu RSHOP, alebo máš záujem sa od nás niečomu priučiť, neváhaj a ozvi sa nám. Ak náhodou na našej kariérnej stránke kariera.riesenia.com nenájdeš svoju vysnívanú poziciu, napíš nám priamo na e-mail joinus@riesenia.com a radi ťa u nás privítame.

+ Diskusia nemá žiadne príspevky