Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to propagate a Vue.js event up the components chain?

I do have three components.

Vue.js dev view

I don't have any influence on what the Datatable component does cause I have it from npm.

Now I want to send an event from EditButton to my Zonelist.

Zonelist component:

<template>
<datatable :columns="table_columns" :data="table_rows" filterable paginate v-on:remove="removeItem"></datatable>
</template>
<script>
import datatable from 'vuejs-datatable';
import moment from 'moment';
export default {
    data() {
        return {
            table_columns: [
                {label: "Zone", component: 'ZoneLink'},
                {label: "Last updated", callback (row) {
                    let locale = $('html').closest('[lang]').attr('lang') || 'en';
                    moment.locale(locale);
                    return moment(row.last_updated).format('D. MMM YYYY');
                }},
                {label: '', component: 'EditButton'}
            ],
            table_rows: [
                {
                    "name": "xyz.de",
                    "last_updated": "2017-10-21 17:29:50"
                }
            ],
            form: {
                name: '',
                errors: []
            }
        };
    },
    components: {
        datatable
    },
    methods: {
            removeItem (item) {
                this.table_rows.forEach((value, index, array) => {
                    if (value.name === item) {
                        Vue.delete(array, index);
                    }
                });
            }
      }
}
</script>

Now my EditButton component $emit()'s the remove event with a parameter.

But nothing happens. So I think vue is not able to locate the listener.

(I'm using method shorthands from ES6 here)

How could I do this properly without mutating Zonelist's state via this.$parent.$parent from the EditButton?

like image 977
jankal Avatar asked Apr 22 '17 13:04

jankal


People also ask

Does Vue emit bubble?

Vue events don't bubble the component tree on their own. However when writing wrapper components this can be the desired behaviour. This code registers a global bubble directive which allows to re-emit all given events: Let's say we want to bubble events start , accelerate and brake of our component Car .

How do I add an event to Vue?

We add an argument to the v-on directive, which will be the name of the event we want to handle. In the above example case, it is a click event. After that, we have to bind an expression to the directive, which will normally be a method you want to use to handle the event. In this case, we've called it clickHandler.


2 Answers

Non parent-child communication in Vue is typically handled via either an event bus, or a state management system.

In this case, unless your application is more complex, the event bus is probably all you need. Since you are using single file components, you may need to declare the bus on the window, probably in your main script.

window.bus = new Vue()

Then in your EditButton, you can emit the event

bus.$emit('some-event', someData)

And in your ZoneList you can listen for it.

bus.$on('some-event', someData => this.doSomething(someData))
like image 199
Bert Avatar answered Sep 17 '22 13:09

Bert


Another option is to ask DataTable to pass any and all events up by adding a v:on="$listeners" attribute to it.

See https://stackoverflow.com/a/61329264/578318 for a more detailed explanation.

Edit:

A much safer option would be to simply listen to the event in the parent class and pass it on...

    <ancestor @message="console.log($event)"> <!-- cute trick see * -->
    ...
    <parent @message="$emit('message', $event)"> <!-- passes it on -->
    ...
    <child @click="$emit('Hello World')"> <!-- creates the event -->

* computed: {'console' : () => console}

like image 26
sparkyspider Avatar answered Sep 17 '22 13:09

sparkyspider