Child component emitting a custom event, but Parent component not seeing it?

What I really want to do here is to have a filter component, a global one that I can reuse across different pages, which upon selecting a filter option from a from a button list or radial list, emits an event that tells the parent which filters were selected, then gets passed as a prop from the parent to a different child that takes uses a vuex getter based on the data from the passed prop.

I don’t really want to use vuex to go through the process of committing a change to a temp array which would change every time the user changes it, and then clear this array when the user changes pages. Just seems like a big waste of resources, but please let me know if this is the way, or if there’s a better way.

Here’s my working child component(emitter confirmed working with devtools), and disclaimer this component is also a global component, and I’ve not yet seen how this effects this situation anywhere in my search;

                    <b-form-radio-group
                        id="btn-radios-3"
                        v-model="selected"
                        :options="options"
                        buttons
                        stacked
                        @click="filterPusher(selected)"
                        name="radio-btn-stacked"
                    ></b-form-radio-group>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    methods: {
        filterPusher(event) {
            return this.$emit("filterchange", event);
        },
    },

Here’s the relevant html of my parent component, and the function that should be called;

<filterProducts
   @filterchange="pushFilter($event)"
/>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    methods: {
        pushFilter(option) {
            console.log(option)
            return this.theFilter = option
        }
    },

I’ve tried using every combo on this listener, calling it like “pushFilter, $event”, or just “pushFilter” and then trying to get the event from the arg in the method. I’ve done no method in the listener and set theFilter manually, no luck. What am I missing here?

Is pushFilter not being called at all, or is the problem just the value of option? Are you seeing no logging or just the wrong value being logged?

My guess would be that click fires too soon, so the selected value being passed to filterPusher will be one render behind. Try clicking multiple times, see what happens. I’m unclear why you’re using a click event rather than driving everything off the data. Personally I would use a computed property with a getter/setter for the v-model and use the setter to emit the filterchange event.

Whatever the problem is, I suggest putting in more console logging to check exactly what’s happening at each stage.

pushFilter isn’t being called at all, so no logging at all. I click multiple buttons to test each time as well.

Sorry I’m still learning the more powerful parts of vue, with this;

Do you mean something like moving the v-model into the getter and just returning the v-model, then set can just return an emit with the v-model arg data passing? Something like;

  computed: {
    onFilterChange: {
      get: function() {
        return this.selected
      },
      set: function(selected) {
        return this.$emit('filterchange', selected)
      }
    }
  },

Would this be what you mean? If not how could I do that?

Yeah, pretty much, with v-model="onFilterChange". Though you wouldn’t usually call it onFilterChange, sounds too much like an event handler. There’s also no need to return anything from the set.

Add in some logging so you can see exactly what’s going on.

You could also try logging the listeners from inside the child, e.g. using a mounted hook:

mounted () {
  console.log(this.$listeners)
}

That should log an object with a filterchange listener in it.

Could you also confirm that:

  1. You aren’t seeing any errors or warnings in console.
  2. You only have two components involved here, with one being the direct child of the other. If there are other components in between or multiple instances of these components on the same page they might be getting in the way.

Added what seems to be the same functionality but with a computed property, more logs and a mounted hook that does infact print a ‘filterchange’ object;

    computed: {
        filterSelect: {
            get: function () {
                console.log(this.selected)
                return this.selected;
            },
            set: function (selected) {
                console.log(selected)
                this.$emit("filterchange", selected);
            },
        },
    },
    mounted() {
        console.log(this.$listeners)
    },

Now opening vue devtools, even though nothing comes to the console(other than the object) it’s logging an emit out, but may be emitting the input multiple times? As an aside would it possible that my form group tries to change the v-model for each button option?

<b-form-group label="Stacked button style radios">
    <b-form-radio-group
        id="btn-radios-3"
        v-model="selected"
        :options="options"
        buttons
        stacked
        name="radio-btn-stacked"
    ></b-form-radio-group>
</b-form-group>

Options is just an array from the bootstrap docs, with three options.

There are only two components involved I fairly certain, I have multiple components on the page, but none of them interact with each other yet.

<div class="no-overflow">
    <b-container fluid>
        <onSale />
    </b-container>
    <b-container class="no-overflow" fluid>
        <b-row>
            <breadcrumb />
        </b-row>
        <b-row>
            <b-col cols="3">
                <div class="sticky-offset">
                    <filterProducts
                        @filterchange="pushFilter($event)"
                    />
                    <b-card>
                        <b-card-text>
                            {{theFilter}}
                        </b-card-text>
                    </b-card>
                </div>
            </b-col>
            <b-col cols="9">
                <inventoryList />
            </b-col>
        </b-row>
    </b-container>
</div>

Excuse some CSS, not my strong suit.

Edit: Using computed like this helped me see that I could use vuex and send an action to mutate an array with the filters inside instead of an emit and then passing a prop. I’ll still be using vuex in the app so would it be wasting resources to get that mutated array and have a computed function in inventoryList that changes the what product data is from the getter based on the mutated array it got?

If you aren’t seeing the console logging then it isn’t working.

This:

v-model="selected"

was supposed to be changed to point at the computed property:

v-model="filterSelect"

I’m not sure exactly where the selected property is defined. Normal for a scenario like this would be for it to be passed in using a prop, so the parent is the source of truth for the data. That doesn’t appear to be the case here, so I’m assuming it’s a data property. In that case the computed property’s setter will also need to update the local selected property.

You can switch all of this to use Vuex if you prefer. The computed property could get and set values from the store. I have nowhere near enough knowledge of your application to judge whether that would be a better design.

1 Like

The v-model change was the culprit! It’s now printing and the parent is running the method(which I’ll likely change to computed)!

Thank you so much for your help!