Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

dkortvelyessy's avatar

vue2 accessing data in slotted components

Greetings all !

I have a blade file in which I wanted to have a general vue component <Mainform /> as the basic form body (handling the name, email, ajax requested aso).

<Mainform lang="en">
	<Subform lang="en" v-bind:maintype="8" />
</Mainform>

In this basic form component, I have a slot.

# mainform.vue
<template>
  <form @submit.prevent="submit">
        <slot @setnewpostext="(newText) => postext = newText"></slot>
  </form>
</tempate>

Here I want to add different selectors, based on the scope of the page. They "generate" a string which should be passed back to the parent <Mainform />. So all have a line such as:

# subform.vue
<script setup>
  const emits = defineEmits(['setnewpostext'])
  const setpostext = ref('')
  const foo = () => { 
     emits('setnewpostext',setpostext.value) 
  }
</script>
<template>
   <input v-model="setpostext" @input="foo"/>
</template>

The rendering works just fine. However, there is no transfer. If I place the Subform/> directly into the <Mainform/> component, then the emitsworks.

I would really like to keep the slot version, as I would rather not have multiple basic forms to worry about.

Any suggestions are more than welcomed :D

Best wishes, Daniel

0 likes
4 replies
LaryAI's avatar
Level 58

To solve the issue of passing data from a slotted component back to the parent component in Vue 2, you need to ensure that the event emitted from the slotted component is properly listened to by the parent component. Here’s a step-by-step solution:

  1. Ensure the slot is named and the event is properly listened to in the parent component.

  2. Update the Mainform component to listen to the event emitted by the slotted component.

  3. Ensure the Subform component emits the event correctly.

Here’s how you can achieve this:

Mainform.vue

<template>
  <form @submit.prevent="submit">
    <!-- Listen to the event from the slot -->
    <slot @setnewpostext="handleNewPostext"></slot>
  </form>
</template>

<script>
export default {
  data() {
    return {
      postext: ''
    };
  },
  methods: {
    handleNewPostext(newText) {
      this.postext = newText;
    },
    submit() {
      // Handle form submission
      console.log('Form submitted with postext:', this.postext);
    }
  }
};
</script>

Subform.vue

<template>
  <input v-model="setpostext" @input="foo" />
</template>

<script>
export default {
  data() {
    return {
      setpostext: ''
    };
  },
  methods: {
    foo() {
      this.$emit('setnewpostext', this.setpostext);
    }
  }
};
</script>

Blade File

<Mainform lang="en">
  <Subform lang="en" v-bind:maintype="8" />
</Mainform>

Explanation:

  1. Mainform.vue:

    • The slot element listens for the setnewpostext event and calls the handleNewPostext method when the event is emitted.
    • The handleNewPostext method updates the postext data property with the new text received from the Subform component.
  2. Subform.vue:

    • The input element is bound to the setpostext data property using v-model.
    • The foo method emits the setnewpostext event with the current value of setpostext whenever the input value changes.
  3. Blade File:

    • The Subform component is used as a slot inside the Mainform component.

By following these steps, the Subform component will correctly emit the setnewpostext event, and the Mainform component will listen to this event and update its postext data property accordingly. This allows you to keep the slot version and avoid having multiple basic forms to manage.

1 like
gych's avatar

You could use Pinia for this or try something like this:

<Mainform lang="en" :postext="postext">
	<Subform lang="en" v-bind:maintype="8" @setnewpostext="(newText) => postext = newText" />
</Mainform>

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

const postext = ref();
</script>

Mainform

<template>
  <form @submit.prevent="submit">
        <slot />
  </form>
</tempate>

<script setup>
	import { watch } from "vue";

	const props = defineProps(['postext']);

	watch(() => props.postext, (newVal, oldVal) => {
   	 	console.log(newVal);
	});
</script>
1 like
dkortvelyessy's avatar
dkortvelyessy
OP
Best Answer
Level 15

Thank you for helping out. With your input, I finally managed it.

In <Mainform> I changed:

<slot @setnewpostext="(newText) => postext = newText"></slot>

to

<slot :onSetNewPostext="(newText) => setpostext = newText"></slot>

Then I called

<Mainform>
   <template #default="{ onSetNewPostext }">
       <Subform
                  lang="en"
                  :maintype="8" 
                  @setnewpostext="onSetNewPostext"
       />
  </template>
</Mainform>

1 like
gych's avatar

@dkortvelyessy No problem :) If your issue has been solved, please don't forget to close your thread by selecting the best answer.

Please or to participate in this conversation.