Search code examples
javascriptvue.jsvuejs2vue-componentvuejs-slots

Use parent slot variables in Vue template slot


I have a simple FormComponent:

<template>
  <form>
    <fieldset>
      <slot />
    </fieldset>
    <span v-if="!isEditing" @click="edit()">Edit</span>
  </form>
</template>

<script>
export default {
  data () {
    return {
      isEditing: false,
    }
  },
  methods: {
    edit (state) {
      this.isEditing = !this.isEditing
    }
  }
}
</script>

And when I use the component:

<FormComponent>
  <input value="Man" type="text" :disabled="!isEditing">
</FormComponent>

The input field are correctly slotted into the component but the :disabled="!isEditing" from the slot isn't reacting to the change of isEditing in the FormComponent.

The Vue documentation is pretty good, but it doesn't cover each edge case.


Solution

  • The component with the <slot></slot> tag has to bind the data onto an attribute on the <slot> tag, like a prop:

    <slot :isEditing="isEditing"></slot>
    

    Then when you render that slotted component, Vue creates and exposes an object containing all of the bound data, with each attribute having a property on that object.

    Access the object by adding an expression to the v-slot directive in this format:

    <FormComponent v-slot:default="slotProps">  
    

    (Or use the alias # as in #default="slotProps".) You can access individual properties like isEditing via that object, like slotProps.isEditing:

    <FormComponent #default="slotProps">
      <input value="Man" type="text" :disabled="!slotProps.isEditing">
    </FormComponent>
    

    Here's the more common (and versatile) <template> syntax:

    <FormComponent>
      <template #default="slotProps">
        <input value="Man" type="text" :disabled="!slotProps.isEditing">
      </template>
    </FormComponent>
    

    You can also destructure slotProps for more direct access to the properties:

    <FormComponent>
      <template #default="{ isEditing }">
        <input value="Man" type="text" :disabled="!isEditing">
      </template>
    </FormComponent>