Uczymy się emitować i przechwytywać własne eventy używając Vue.js. Do dzieła.
Tworzymy nową apkę za pomocą Vue CLI, to już umiemy. Tworzymy nowy komponent:
<template >
<h2>bla bla bla</h2>
<button @click="$emit('abc')">abc</button>
</template>
<script>
export default {
emits: ['abc'],
};
</script>
Ten komponent emituje event za pomocą $emit + nazwa eventu. Event musi znaleźć się w emits. Teraz dodajmy ten komponent do App.vue i NA TYM ELEMENCIE przechwytujemy ten event:
<template>
<img alt="Vue logo" src="./assets/logo.png" >
<HelloWorld msg="Welcome to Your Vue.js App"/>
<NewComponent @abc="abcHandler"></NewComponent>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import NewComponent from './components/NewComponent.vue';
export default {
name: 'App',
components: {
HelloWorld,
NewComponent
},
methods: {
abcHandler(){
alert("bla bla bla abc");
},
}
}
</script>
Ok, działa. Co nie znaczy, że wszystko będzie działało tak jak w zwykłych eventach. Po pierwsze – dlaczego tak?
Cóż, separacja kodu. Pozwalamy w NewComponent.vue określić, że coś zawsze będzie event emitowało, ale sposób, w jaki ten event zostanie obsłużony (listener, handler) pozostawiamy użytkownikowi danego komponentu. Nie możemy przecież na poziomie komponentu zrobić handlera, który będzie radził sobie ze zmianami na poziomie aplikacji.
Nawet jeśli popsujemy modularność, reużywalność i inne buzzwordy i wrzucimy listener i handler, który jakoś będzie radził sobie ze zmianami na poziomie aplikacji, to to chyba nie jest dobre do tego miejsce.
Po drugie – sama struktura Vue JS. Zobaczmy, że na NewComponent nie możemy sobie dać tak po prostu @click. Nie jest to web-component utworzony w Stencilu/czystym JS, jest to swego rodzaju koncepcja. Jak zobaczymy HTML, to zauważymy, że tam nie ma „NewComponent”. Niby co zatem powinno być kliknięte i co powinno słuchać?
Tak, w markupie NewComponent możemy zrobić @click (v-on:click) na tagu HTML, który wyemituje event, na który NewComponent może nasłuchiwać przez @custom-evt.
Po trzecie, we frontnedowych frameworkach jest koncepcja komunikacji rodzic-dzieci. Tzw. jednostronny przepływ danych. Bardzo często zapewniany przez props, choć one nie mogą się zmieniać ani „iść w górę”. W każdym razie nie na naszym poziomie zaawansowania.
Istnieje sposób, aby coś przekazywać od „przodka” do „wnuków”/”potomków” pomijając prop drilling czyli przekazywanie w dół przez każdego rodzica (ten sposób nazywa się provide/inject), to jeszcze poznamy.
Jeżeli chodzi o custom eventy, to emitują je elementy HTML, wewnątrz komponentu. Event propaguje aż do komponentu-rodzica (NewComponent), który może słuchać tego eventu.
Ważna uwaga – eventy nie propagują dalej, niż ich rodzic-komponent. Oznacza to, że rodzic NewComponent nie będzie w stanie przechwycić tego eventu, tylko on sam. Możemy to sobie sprawdzić.
Na razie nie jesteśmy na tyle sprawni, aby rozwiązać wszystkie problemy związane z eventami, ale możemy poznać kilka sztuczek.
Zróbmy tak, aby nasz komponent przyjmował propsy i wysyłał argumenty do emitowania:
<template >
<h2>Name: {{ name }}</h2>
<button @click="$emit('abc', name)">Click me</button>
</template>
<script>
export default {
emits: ['abc'],
props: ['name']
};
</script>
Ok, teraz po stronie App pewne zmiany:
<template>
<img alt="Vue logo" src="./assets/logo.png" >
<HelloWorld msg="Welcome to Your Vue.js App"/>
<NewComponent name="John Doe" @abc="abcHandler"></NewComponent>
<NewComponent name="Jane Doe" @abc="abcHandler"></NewComponent>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import NewComponent from './components/NewComponent.vue';
export default {
name: 'App',
components: {
HelloWorld,
NewComponent
},
methods: {
abcHandler(name){
alert(name);
},
}
}
</script>
Działa. Przekazuje name. Ok, alternatywny sposób na emit:
<template >
<h2>Name: {{ name }}</h2>
<button @click="clickHandler">Click me</button>
</template>
<script>
export default {
emits: ['abc'],
props: ['name'],
methods: {
clickHandler(){
this.$emit('abc', this.name)
}
},
};
</script>
Więcej Vue niedługo.