1

I am using Vue 2 and I'm trying to have a parent component pass in multiple values to the child, and have the child return back a specific object to the parent. It needs to be two way data binding for the data.

For example I have this school object:

  school: {
    name: "Tech School",
    type: "highschool"
  }

A list of students with different properties: (The list can be huge, but this is pseudo code)

  data () {
    return {
      totalStudents: [{
        school: {
          name: "Tech School",
          type: "highschool"
         },
        studentId: 123,
        city: "seattle"
        },
        school: {
          name: "Art School",
          type: "university"
         }
        studentId: 747,
        city: "toronto"
        },
      }],
   } 
  }

The parent calls this custom child component (I'm not sure how to write this part)

     <CustomChildPicker :value="prop" @input="prop=
           totalStudents[index].school,    // trying to pass these 3 fields from parent to child
           totalStudents[index].studentId,
           totalStudents[index].city"
           v-model=totalStudents[index].school //only want this updated data back from child
      />

How do we pass in dynamic data to the child component, but only have the school object have two way data binding with the parent?

I want to pass data from the parent to the child, and the child does some conversions to that data and pass the converted data back to the parent. (school object)

But also if the data in the parent changes, the child gets the updated changed data, redo the conversions above and give the updated data back to the parent. (if the student's city changes, then their school also changes)

How would we do this? Thanks for the help!

Suzy
  • 231
  • 4
  • 14
  • 1
    You may want to look at creating a custom v-model on the child component https://paulund.co.uk/use-v-model-on-custom-vue-component – dantheman Jun 09 '21 at 18:01
  • Custom `v-model` is definitely an option, you can also just use props and an event too if you don't care about specifically using `v-model`. – zcoop98 Jun 09 '21 at 21:43

2 Answers2

1

In Vue there's a "mantra": "props go down, events go up".

This means that you have to create the "cycle" of two-way binding by using v-bind -> props (parent -> child) & $emit -> v-on (child -> parent)

Vue.component('ChildComponent', {
  // receiving data from parent v-bind
  props: ['this', 'going'],
  computed: {
    // this is the reactive data we use in the child
    propsToParent: {
      get() {
        return {
          this: this.this,
          going: this.going
        }
      },
      set(val) {
        this.$emit("update:parent-data", val)
      }
    }
  },
  data() {
    return {
      // only for the snippet (toggle)
      toggleUp: true,
    }
  },
  methods: {
    // here you can do calculations in the child component
    emitData() {
      this.propsToParent = {
        ...this.propsToParent,
        going: this.toggleUp ? 'up' : 'down'
      }
      // only for the snippet (toggle)
      this.toggleUp = !this.toggleUp
    }
  },
  template: `
    <div>
      PROPS FROM PARENT: {{ propsToParent }}<br />
      <button
        @click="emitData"
      >
        SEND TO PARENT
      </button>
    </div>
  `
})
new Vue({
  el: "#app",
  data() {
    return {
      dataDown: {
        this: "is",
        going: "down",
      },
    }
  },
  methods: {
    updateDataDown(val) {
      this.dataDown = { ...this.dataDown,
        ...val
      }
    }
  },
  template: `
    <div>
      PARENT DOWN: {{ dataDown }}
      <hr>
      <ChildComponent
        v-bind="dataDown"
        v-on:update:parent-data="(val) => updateDataDown(val)"
      />
    </div>
  `
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
muka.gergely
  • 8,063
  • 2
  • 17
  • 34
1

The manual props/ events way has already been shown, so I'll add how to setup and use a custom v-model. It's underpinned by the exact same props-down-events-up mechanism, but it can potentially make your template cleaner, if you prefer.

v-model in Vue is, by default, setup to use the value prop for input and the input event for output. But we can change these to anything we want to using Vue's model option:

model: {
  prop: 'school',
  event: 'click'
},

So in your case, since you're wanting school to be the v-model value, we can setup the internals of your component like this, configuring a prop for each value you want to pass to the child, and configuring which prop and event you want v-model to use in the model option:

default export {
  name: "CustomChildPicker",
  // Specify props you want your component to accept:
  props: {
    school: Object,
    studentId: Number,
    city: String,
  },
  // Specify which prop and event Vue should use for v-model:
  model: {
    prop: 'school',
    event: 'schoolUpdate' // Can be anything, custom or otherwise
  },
  // Other options as needed:
  data() { return {. . .}; },
  methods: {. . .},
  computed: {. . .},
  watch: {. . .},
  // Etc.
}

Which you can then use in a template like this:

<CustomChildPicker v-model="totalStudents[index].school"
  :studentId="totalStudents[index].studentId"
  :city="totalStudents[index].city"
/>

Important note: You'll need to include a call to $emit() somewhere in your child component to manually emit the event triggering your v-model to update. Just make sure to wire up the event's value correctly, so that it actually emits whatever the new school value should be:

this.$emit('schoolUpdate', *new_school_value*);

In this example, *new_school_value* is just a stand-in for whatever your actual updated school value is, you'll need to manually provide it as $emit()'s second argument.

You can put this call wherever it makes sense in your component, but the second parameter of $emit() needs to be the updated value of school, whatever that looks like in your usage, since this is the value that will be stored in the parent via v-model.

tony19
  • 125,647
  • 18
  • 229
  • 307
zcoop98
  • 2,590
  • 1
  • 18
  • 31
  • thank you! where does `new_school_value` get set? – Suzy Jun 09 '21 at 22:20
  • @Suzy Updated the explanation surrounding emit to hopefully be a little clearer! You'll need to provide the *actual* updated school value as `$emit`'s second argument, however that looks in your actual code. The point is that the `$emit` call is how you communicate the value back to the parent via `v-model`. – zcoop98 Jun 09 '21 at 22:33