2

I am trying to show a loading indicator which is located inside of a component that contains a slot element (lets call this the wrapper component). To do this, I have a function inside the wrapper that sets the state of the indicator based on an input boolean (setSpinnerVisible()). Now, I would like to execute this function from the component that uses this wrapper. To do this, in the parent component I use the v-slot property to get a reference to the function. I would like to be able to call this function inside the mounted() function, or from a function within methods.

However, I am not able to figure out how to do this. The only way I can think of is by passing this v-slot value into a function that is executed on an event like a button press, which works, but I also want to be able to call this method from a function that is not executed by an action in the layout (e.g. in the mounted() function).

This is (a part of) my wrapper component (the function that toggles the spinner is left out for brevity):

<template>
    <slot v-bind:setSpinnerVisible="setSpinnerVisible"></slot>
    ...
    <div class="spinner" v-show="spinnerVisible"></div>
</template>

This is (a part of) the component that uses the wrapper:

<Wrapper v-slot="{ setSpinnerVisible }">
    ...
</Wrapper>

I would like to be able to use the value of setSpinnerVisible inside the mounted function in one way or another, something like this fictional piece of code:

<script>
export default {
    mounted() {
        this.setSpinnerVisible(true)
    }
}
</script>

I am using Vue 2.6.11

Martin Brisiak
  • 3,872
  • 12
  • 37
  • 51
Max Stevens
  • 112
  • 8
  • Have you considered $refs? https://vuejs.org/v2/guide/components-edge-cases.html#Accessing-Child-Component-Instances-amp-Child-Elements – Reinier68 Jan 18 '21 at 13:42
  • 1
    @Reinier68 No I did not, but it seems like a good option for my use case. Thank you! – Max Stevens Jan 18 '21 at 13:56

1 Answers1

0

There are several approaches you could take.

  1. For example, you could access the parent instance and call the method you need:

    this.$parent.setSpinnerVisible()

  2. Alternatively, you could create a gateway component that uses the Wrapper, gets setSpinnerVisible and passes it as a prop to the component that needs it.

  3. You can use dependency injection. Described here: https://v2.vuejs.org/v2/guide/components-edge-cases.html#Dependency-Injection

So, in Wrapper.vue

<template>
    ...
</template>
<script>
export default {
    provide () {
        return {
            setSpinnerVisible: this.setSpinnerVisible
        }
    }
}
</script>

And in your child component:

<Wrapper>
    ...
</Wrapper>
<script>
export default {
    inject: ['setSpinnerVisible'],
    mounted() {
        this.setSpinnerVisible(true)
    }
}
</script>

The last one would be my recommended approach because it's much neater and is not anti-pattern.

tony19
  • 125,647
  • 18
  • 229
  • 307
Itope84
  • 471
  • 5
  • 7
  • Your code seems like a good and robust approach, but for my usecase it requires me to put the contents of the wrapper in its own component, which creates an (for now) seemingly unnecessary abstraction. – Max Stevens Jan 18 '21 at 20:45