Пользовательское событие, не захваченное компонентом в Vue.js

Вот моя архитектура компонентов

введите здесь описание изображения

Компонент <ListButtonMenu> генерирует пользовательское событие display-error, как показано ниже. Эмиссия работает нормально, так как я вижу событие в VueDevtools:

<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.$emit('display-error');
        }
    }
}
</script>

введите здесь описание изображения

Компонент <ErrorMessage> содержит элемент div, который прослушивает пользовательское событие и вызывает метод display(). Метод изменяет класс CSS компонента, чтобы отобразить самого себя: v-on:display-error="display". К сожалению, это не работает, и класс CSS не изменяется. Что мне не хватает?

<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 type="button" class="close" data-dismiss="alert" aria-hidden="true">
                &times;
            </button>
        </div>
    </div>
</template>

<script>
export default {
    data: function () {
        return {
            'cssClass': 'd-none',
            'errorMessage': 'Hello'
        }
    },
    methods: {
        display() {
            this.cssClass = 'show';
            this.errorMessage='Goodbye';
        }
    }
}
</script>

person Alexis.Rolland    schedule 27.01.2019    source источник


Ответы (2)


Слушатели ошибок должны быть прикреплены к элементу, генерирующему событие. Когда у вас есть <div id="errorMessage" v-bind:class="cssClass" v-on:display-error="display()">, это добавляет прослушиватель, когда этот div испускает событие, но событие не приходит из div, оно исходит от вашего ListButtonMenu. Вам нужно добавить директиву v-on к этому элементу следующим образом:

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

Редактировать:

Ваш элемент ErrorMessage должен принимать ошибки от родителя, подобного этому (обратите внимание на добавленный $emit для кнопки закрытия):

<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>

И тогда ваш родитель может выглядеть примерно так:

<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>
person 3ocene    schedule 27.01.2019
comment
Спасибо, я добавил v-on="display()" вот так <list-button-menu v-on="display()"></list-button-menu>, который на самом деле находится в компоненте <ListForm>. Это не работает, потому что <ListForm> не имеет никакого display() метода. Я также обновил <ListButtonMenu> выше, чтобы показать полный код. Не могли бы вы взглянуть на это и сказать мне, если я должен что-то изменить там? - person Alexis.Rolland; 27.01.2019
comment
Прежде чем я опубликую обновление, подумайте о том, как вы можете передать значения в этот элемент ошибки из родительского элемента. - person 3ocene; 27.01.2019
comment
Извините, я не понял, что вы имеете в виду. На какой элемент вы ссылаетесь, когда говорите об этом элементе ошибки? На самом деле компонент <ErrorMessage> на самом деле не является родителем <ListButtonAttribute>. Он просто выше в иерархии. Является ли это проблемой, и только родители компонента могут захватывать события от дочерних компонентов? Кроме того, может ли дедушка-родитель записывать события внуков или только их непосредственных детей? - person Alexis.Rolland; 27.01.2019
comment
У вас двое братьев и сестер, поэтому ваш родитель должен способствовать общению. И под этим элементом ошибки я подразумеваю ваш компонент ошибки. Каждый раз, когда я говорил элемент, я имел в виду компонент. - person 3ocene; 27.01.2019
comment
Я понял, спасибо за подробный ответ. В основном и в соответствии с архитектурой компонентов, которую я описал выше, информация об ошибке должна идти от ListButtonMenuListFormListAppErrorMessage. Я думаю, что я переработаю архитектуру, чтобы сделать ее более простой. - person Alexis.Rolland; 27.01.2019

Поскольку мои компоненты ErrorMessage и ListButtonMenu не имеют отношения родитель-потомок, я выбрал решение, отличное от использования пользовательских событий.

Вместо этого я использую свое состояние приложения (построенное с использованием хранилища Vuex) в качестве шины событий для обновления errorObject, а компонент ErrorMessage использует вычисляемые свойства для захвата errorObject и отображает себя.

Моя компонентная архитектура остается такой же, как и в вопросе. Вот файл store.js:

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

Vue.use(Vuex);

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

Вот компонент ListButtonMenu:

<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>

А вот и компонент ErrorMessage;

<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>
person Alexis.Rolland    schedule 27.01.2019