1

I'm breaking my head for a few days now, trying to figure out how to grab the data from child components.

Situation is like this.

I have one parent component called Post where user can select date, title, description and which can contain multiple instances of Event compontents. Event component contains fields like title, description, attendees.

User should be able to add multiple Eventcomponents which means I have multiple components Event within the Post component.

So, I can't figure out how can I compose my components to have an array of Event objects inside my Post component which I can later on send to my API.

the structure of the post object I need is:

// Post.vue
{ 
    "date": '',
    "name": '',
    "description": '',
    "events": {
       {
        "title": '',
        "description": '',
        "attendees": ''
       },
       {
        "title": '',
        "description": '',
        "attendees": ''
       }
    }
}

So, I don't know should and how I would use vuex for it. I've tried using $emit to pass the data but I couldn't find it fit to get the data into Post model.

Can someone point me where should I look for it?

EDIT #1: Added sample code

The code for the components:

<template>
  <v-form>
    <v-container>
      <v-row>
        <v-col
          cols="12"
          md="4"
        >
          <v-date-picker v-model="post.date" scrollable>
            <v-spacer />
            <v-btn text color="primary" @click="modal = false">
              Cancel
            </v-btn>
            <v-btn text color="primary" @click="$refs.dialog.save(date)">
              OK
            </v-btn>
          </v-date-picker>
        </v-col>

        <v-col
          cols="12"
          md="4"
        >
          <v-text-field
            v-model="post.name"
            label="name"
            required
          />
        </v-col>

        <v-col
          cols="12"
          md="4"
        >
          <v-textarea
            v-model="post.description"
            name="description"
            label="Description"
            dense
            value
            rows="4"
            hint
          />
        </v-col>
      </v-row>
      <v-row>
        <v-btn primary rounded @click="addLine">
          Add Event
        </v-btn>
        <v-expansion-panels accordion>
          <UserEvent
            v-for="(line, index) in lines"
            :key="index"
            @addLine="addLine"
            @removeLine="removeLine(index)"
          />
        </v-expansion-panels>
      </v-row>
    </v-container>
  </v-form>
</template>

<script>
import UserEvent from './partials/event'
export default {
  name: 'Post',
  components: { UserEvent },
  data () {
    return {
      post: [],
      lines: [],
      blockRemoval: true
    }
  },
  watch: {
    lines () {
      this.blockRemoval = this.lines.length <= 1
    }
  },
  mounted () {
  },
  methods: {
    addLine () {
      const checkEmptyLines = this.lines.filter(line => line.number === null)
      if (checkEmptyLines.length >= 1 && this.lines.length > 0) { return }
      this.lines.push({
        title: null,
        description: null,
        attendees: null
      })
    },
    removeLine (lineId) {
      if (!this.blockRemoval) { this.lines.splice(lineId, 1) }
    }
  }
}
</script>

And the child component UserEvent

// UserEvent.vue
<template>
  <v-expansion-panel>
    <v-expansion-panel-header>Event details</v-expansion-panel-header>
    <v-expansion-panel-content>
      <v-row>
        <v-col cols="12" md="6">
          <v-text-field
            v-model="event.title"
            label="Title"
            required
          />
        </v-col>

        <v-col
          cols="12"
          md="6"
        >
          <v-text-field
            v-model="event.atttendees"
            label="Atendees"
            required
          />
        </v-col>

        <v-col
          cols="12"
          md="12"
        >
          <v-textarea
            v-model="event.description"
            name="description"
            label="Description"
            dense
            value
            rows="4"
            hint
          />
        </v-col>
        <v-col
          cols="12"
          md="3"
        >
          <div class="block float-right">
            <v-btn @click="removeLine(index)" />
            <v-btn v-if="index + 1 === lines.length" @click="addLine" />
          </div>
        </v-col>
      </v-row>
    </v-expansion-panel-content>
  </v-expansion-panel>
</template>

<script>
export default {
  name: 'UserEvent',
  props: ['line', 'index'],
  data () {
    return {
      event: []
    }
  },
  methods: {
    addLine () {
      this.$emit('addLine')
    },
    removeLine (index) {
      this.$emit('removeLine', index)
    }
  }
}
</script>
4
  • 3
    The child elements could $emit each time they have new data. The parent could collect that data and post as needed to a backend. This is about as specific as I can be without a code sample. Commented Oct 13, 2019 at 16:59
  • Hi David, I've updated the question with the sample code. What I'd like to know is: Is it possible to keep the UserEvents in sync with the parent component, or I can update the parent component only by using this.$emit events? Commented Oct 14, 2019 at 12:27
  • 1
    The parent component Post should be the only one responsible for holding the state. The children should use v-model to transmit the user input to Post. Take a look at : vuejs.org/v2/guide/components.html#Using-v-model-on-Components (you should think about the Event component as a custom input) Commented Oct 14, 2019 at 13:20
  • I thought of that as well. I'm just confused about how to make sure that the proper event is updated or removed from the Post store. Commented Oct 14, 2019 at 15:50

1 Answer 1

1

Here's an example with a similar structure what was posed in the question:

{
  name: String,
  events: [
    title: String,
    description: String,
  ],
}

This example allows the user to open a form to add a new event. When that form is submitted, the event data is added to the parent component's state.

Parent

<template>
  <div>
    <input v-model="name" />
    <ul v-if="events.length">
      <li v-for="(event, index) in events" :key="index">
        <span>{{ event.title }}</span>
        <span>{{ event.description }}</span>
      </li>
    </ul>
    <Event v-if="isNewEventFormVisible" @submit="addEvent" />
    <button v-else @click="showNewEventForm">add event</button>
  </div>
</template>
import Event from '~/components/Event';

export default {
  components: { Event },
  data() {
    return {
      name: 'Example Post',
      events: [],
      isNewEventFormVisible: false,
    };
  },
  methods: {
    addEvent({ title, description }) {
      this.isNewEventFormVisible = false;
      this.events.push({ title, description });
      // TODO: call you API here to update
    },
    showNewEventForm() {
      this.isNewEventFormVisible = true;
    },
  },
};

Event

<template>
  <form @submit.prevent="onSubmit">
    <input v-model.trim="title" type="text" />
    <br />
    <textarea v-model.trim="description" />
    <button type="submit">submit</button>
  </form>
</template>
export default {
  data() {
    return {
      title: '',
      description: '',
    };
  },
  methods: {
    onSubmit() {
      this.$emit('submit', {
        title: this.title,
        description: this.description,
      });
    },
  },
};

You could imagine a more sophisticated version of this where events are editable. In that case, each Event could take props and bind them as values to its input instead of using v-models.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.