/ Gists / Ref slot (Options vs Composition, Parent / Child, Child / Parent)
On gists

Ref slot (Options vs Composition, Parent / Child, Child / Parent)

Popular ⭐ Vue.js

ChildToParent-ScopedSlot.vue Raw #

<!-- 1) From Child to Parent via scoped slot and dynamic ref-->

<!-- App.vue-->
<template>
  <div id="app">
    <HelloWorld v-slot="{ setRef }">
      <input type="text" :ref="(el) => setRef(el)" />
    </HelloWorld>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: 'App',
  components: {
    HelloWorld,
  },
};
</script>


<!-- Hello world -->
<template>
    <slot v-bind="{ setRef }"></slot>
    <button @click="setFocus">SET</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      ref: ''
    }
  },
  methods: {
    setFocus()
    {
      this.ref.focus()
    },
    setRef(el) {
     this.ref = el
     console.log(el)
    }
  }
}
</script>

ParentToChild.vue Raw #

<!-- 2) From parent to child via method that set ref -->
<!-- App.vue -->
<template>
  <div id="app">
    <HelloWorld name="child" :b="setRefOnA">
      <input type="text" ref="a" value="default value of this input" />
    </HelloWorld>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: 'App',
  components: {
    HelloWorld,
  },
  methods: {
    setRefOnA(el) {
      return this.$refs.a;
    },
  },
};
</script>


<!-- HelloWorld.vue -->
<template>
  <div>
    <slot> ... </slot>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: ['b'],
  mounted() {
    console.log(this.b().focus());
  },
};
</script>

Composition-Child-to-Parent.vue Raw #

<!-- Same as 1 but in Composition API -->
<!-- App.vue -->
<script setup>
import SlotVue from "./Slot.vue"
</script>

<template>
  <SlotVue>
    <template #child="{ setRef }">
   		<button :ref="(el) => setRef(el)">
     		child
   		</button>
			</template>
  </SlotVue>
</template>

<!-- Slot.vue - child -->
<script setup>
import { ref } from 'vue'

const slotRef = ref()
const setSlotRef = (el) => {
  console.log(el, "xxcc") // only once in assign
  slotRef.value = el;
}

const onClick = () => {
  console.log(slotRef.value)
}
</script>

<template>
  <p>
    press f12 to open a developer tools
  </p>
  <slot name="child" :set-ref="setSlotRef"/>
  <button @click="onClick">
    console.log slot
  </button>
</template>

ParentToChild-computed-methods.j Raw #

<template>
  {{ computedRef }}
  <hr />
  <div v-if="myProp">
    {{ computedRefButton }}
  </div>

  <hr />
  <Foo :heading="refHeading" :textarea="refTextarea" :section="computedRef">
    <section ref="computedRef">
      <h2 ref="heading">Heading</h2>
      <textarea ref="textarea"></textarea>
      <br />
      <button ref="button">test</button>
    </section>
  </Foo>
</template>

<script>
import Foo from './components/Foo.vue';

export default {
  name: 'App',
  components: {
    Foo,
  },
  data() {
    return {
      isMounted: false,
      myProp: true,
    };
  },
  computed: {
    computedRef() {
      if (!this.isMounted) return;
      console.log(this.$refs.computedRef);
      return this.$refs.computedRef;
    },
    computedRefButton() {
      return this.$refs.button;
    },
  },
  methods: {
    refHeading() {
      return this.$refs.heading;
    },
    refTextarea() {
      return this.$refs.textarea;
    },
  },

  mounted() {
    this.isMounted = true;
  },

  updated() {
    console.log(this.$refs);
  },
};
</script>
  
  
// Child.vue
<template>
  <div>
    <slot> ... </slot>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: ['b'],
  mounted() {
    console.log(this.b().focus());
  },
};
</script>

CompositionApi.vue Raw #

// PARENT -> CHILD

// PARENT
<!-- App.vue-->
<template>

    <HelloWorld v-slot="{ setRef }">
      <input type="text" :ref="(el) => setRef(el)" />
    </HelloWorld>
 
</template>

<script setup>
import HelloWorld from './HelloWorld.vue';
</script>


// CHILD
<template>
  <slot v-bind="{ setRef }"></slot>
  <button @click="setFocus">SET</button>
</template>

<script setup>
import { ref } from 'vue';

const myInputFromSlot = ref(null); // what ever name

const setFocus = () => {
  myInputFromSlot.value.focus();
};

const setRef = (el) => {
  myInputFromSlot.value = el
  console.log(el);
};

</script>



// CHILD -> PARENT

// PARENT
<!-- App.vue-->
<template>

    <HelloWorld :ref-to-child="setRefToChild">
      <input type="text" ref="inputak" />
    </HelloWorld>
 
</template>

<script setup>
import { ref } from "vue" 
import HelloWorld from './HelloWorld.vue';

const inputak = ref()
const setRefToChild = () => inputak
</script>


// CHILD
<template>
  <slot></slot>
</template>

<script setup>
import { onMounted } from 'vue'
const props = defineProps(['refToChild'])

onMounted(() => {
  console.log(props.refToChild().value.focus())
})

</script>

ParentToChildWithFullReactivity. Raw #

<!-- UMI REAKTIVITU, musi byt predany objekt! -->

<!-- Parent Usage -->
 <recipe-row-marker v-slot="{ toggle }">
  <input
    v-model="toggle.isDone"
    type="checkbox" 
    class="form-control" 
    />
</recipe-row-marker>


<!-- RecipeRowMarker.vue -->
<template>
  <slot v-bind="{ toggle }" />
</template>

<script setup>
import { ref } from 'vue'
const toggle = ref({
  isDone: false
})
</script>

<style scoped>
:slotted(.IsDone :where(.RecipeStep__Heading, .RecipeStep__Text)) {
  @apply opacity-50 line-through;
}
</style>