Home > database >  VueJs 3 emit event from grand child to his grand parent component
VueJs 3 emit event from grand child to his grand parent component

Time:10-12

I'm relatively new to Vue and working on my first project. I'm working on creating a form with several children and grandchildren components. I ran into an issue where I will need the ability to generate multiple copies of a form. Therefore I moved some data properties up 1 level. Currently, the form is ApplicationPage.Vue > TheApplication.Vue > PersonalInformation.Vue > BaseInput.Vue. My issue is I need to emit changes from PersonalInformation to ApplicationPage passing through TheApplication. I'm having a hard time figuring out how to handle this situation. I keep finding solutions for Vue2 but not for Vue3.

ApplicationPage.vue

template
      <TheApplication :petOptions="petOptions"
                      :stateOptions='stateOptions'
                      v-model="data.primary"
                      applicant="Primary"/>

script
data() {
    return {
      data: {
        primary: {
          personalInformation: {
            first_name: "",
            middle_name: "",
            last_name: "",
            date_of_birth: "",
            phone: null,
            email: "",
            pets: "",
            driver_license: null,
            driver_license_state: "",
            number_of_pets: null,
            additional_comments: ""
          },
        },
      },
    }
  },

TheApplication.Vue

            <personal-information :petOptions="petOptions"
                                  :stateOptions='stateOptions'
                                  :personalInformation="modelValue.personalInformation"
                                  @updateField="UpdateField"
            />
  methods: {
    UpdateField(field, value) {
      this.$emit('update:modelValue', {...this.modelValue, [field]: value})
    },

PersonalInformation.vue

      <base-input :value="personalInformation.first_name"
                  @change="onInput('personalInformation.first_name', $event.target.value)"
                  label="First Name*"
                  type="text" 
                  required/>
methods: {
    onInput(field, value) {
      console.log(field   " "   value)
      // this.$emit('updateField', { ...this.personalInformation, [field]: value })
      this.$emit('updateField', field, value)
    },
  }

CodePudding user response:

This is how I would do it: codesandbox

Emits only take two parameters, the name of the emit and the value being emitted. If emitting more than one value you have to emit the values as a single object. In my solution, the grandchild component emits the field name and the value as a single object

Grandchild

<input
    :value="personalInformation.first_name"
    @input="onInput('first_name', $event.target.value)"
    ...
>
onInput(field, value) {
  this.$emit("update-field", { field: field, value: value });
},

Which the child object catches and re-emits, but first takes care to emit in the format expected by the parent component (it expects the entire data.primary object since that is what was set as the v-model)

Child

<grandchild
    :personalInformation="modelValue.personalInformation"
    @updateField="UpdateField"
  />
UpdateField({ field, value }) {
  const newVal = this.modelValue;
  newVal.personalInformation[field] = value;
  this.$emit("update:modelValue", newVal);
}

The parent component then automatically receives and updates the v-model data.primary object.

Alternatively, I have to mention that instead of dealing with any emitting / passing down objects between multiple levels of components, you can always use Pinia, the official state management library for Vue (save some state in one component, read same state from any other component). There's of course a learning curve but it's definitely worth learning and is made to simplify exactly this type of situation.

  • Related