[Vuejs]-Custom event not captured by component in Vue.js

0👍

Because my ErrorMessage and ListButtonMenu components do not have a parent-child relationship, I opted for a different solution than using custom events.

Instead, I am using my application state (built using Vuex store) as an event bus to update an errorObject and the ErrorMessage component uses computed properties to capture the errorObject and displays himself.

My component architecture remains the same as in the question.
Here is the file store.js:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        errorObject: {
            flag: false,
            message: ''
        }
    }
});

Here is the ListButtonMenu component:

<template>
    <div>
        <button type="button" class="btn btn-success" v-on:click="saveList()">
            Save
        </button>
    </div>
</template>

<script>
export default {
    props: {
        list: Object
    },
    methods: {
        saveList() {
            this.$store.state.errorObject.flag = true;
            this.$store.state.errorObject.message = 'My error mesage';
        }
    }
}
</script>

And here the ErrorMessage component;

<template>
    <div id="errorMessage" v-bind:class="cssClass">
        <div class="row justify-content-center alert alert-danger alert-dismissable text-danger">
            Error: {{ errorMessage }}
            <button type="button" class="close" v-on:click="close()">
                &times;
            </button>
        </div>
    </div>
</template>

<script>
export default {
    data: function () {
        return {
        }
    },
    computed: {
        cssClass() {
            if (this.$store.state.errorObject.flag) { return 'display' }
            else { return 'd-none' }
        },
        errorMessage() {
            return this.$store.state.errorObject.message;
        }
    },
    methods: {
        close() {
            this.$store.state.errorObject.flag = false;
            this.$store.state.errorObject.message= '';
        }
    }
}
</script>

0👍

Error listeners must be attached to the element emitting the event. When you have <div id="errorMessage" v-bind:class="cssClass" v-on:display-error="display()">, this adds a listener for when that div emits the event, but the event doesn’t come form the div, it comes from your ListButtonMenu. You need to add the v-on directive to that element like so:

<ListButtonMenu v-on="display()" other="attributes"></ListButtonMenu>

Edit:

Your ErrorMessage element needs to accept errors from a parent like this (note the added $emit for the close button):

<template>
  <div id="errorMessage" v-bind:class="cssClass" v-on:display-error="display()">
    <div class="row justify-content-center alert alert-danger alert-dismissable text-danger">
      Error: {{ errorMessage }}
      <button v-on:click="$emit('input', false)" type="button" class="close" data-dismiss="alert" aria-hidden="true">
      &times;
      </button>
    </div>
  </div>
</template>

<script>
export default {
  props: [
    'errorMessage',
    'value', // Generally value is used to toggle things so that v-model works
  ],
  computed: {
    cssClass() {
      return this.value ? 'show' : 'd-none';
    }
  }
  methods: {
    display() {
      this.cssClass = 'show';
      this.errorMessage='Goodbye';
    }
  }
}
</script>

And then your parent can look something like this:

<template>
  <error-message v-model="showError" v-bind:errorMessage="errorMessage">
  <list-button-menu v-on:display-error="displayError()"></list-button-menu>
</template>

<script>
export default {
  data: function() {
    return {
      errorMessage: 'Hello',
      showError: false,
    }
  },
  methods: {
    displayError() {
      this.showError = true;
      this.errorMessage: 'Goodbye';
    }
  }
}
</script>
👤3ocene

Leave a comment