Grandparent component in VueJs 3 sends events to its grandparent component
P粉251903163
P粉251903163 2023-11-03 16:03:06
0
2
790

I am relatively new to Vue and working on my first project. I'm trying to create a form with multiple child and grandchild components. I've run into a problem where I need to be able to generate multiple copies of a form. Therefore, I moved some data attributes up 1 level. Currently, the form is ApplicationPage.Vue > TheApplication.Vue > PersonalInformation.Vue > BaseInput.Vue. My problem is that I need to emit changes from PersonalInformation to ApplicationPage via TheApplication . I'm having a hard time figuring out how to handle this situation. I've been looking for a solution for Vue2 but haven't found a solution 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})
    },

Personal information.vue

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


P粉251903163
P粉251903163

reply all(2)
P粉037880905

For anyone who doesn't want to chain event emitting, there is a parent object on the child object which can also be used to emit events. Be sure to register the launch in the parent to avoid warnings in the console.

Subitems

Call the direct parent's $emit here.

Child.vue

<input @input="$parent.$emit('custom-event', e.target.value) />

Or how to use:

<input @input="handleInput" />
export default {
  methods: {
    handleInput(e) {
      this.$parent.$emit('custom-event', e.target.value)
    }
  }
}

Father

Since it is the parent that signals the ancestor, declare the emission here. For , just use the defineEmits() method to declare emits. See Documentation.

Parent.vue

<Child /> <!-- No need to listen to the event here -->
export default {
   emits: ['custom-event'] // Register the emits
}

If using

<script setup>
  defineEmits(['custom-event']) // Register the emits
</script>

grandparents

Then listen to the event in the grandparent component.

GrandParent.vue

<Parent @custom-event="doSomething()" /> <!-- The event is being listened to in the grandparent component -->
P粉842215006

This is how I do it: codesandbox

Emits accepts only two parameters, the emitted name and the emitted value. If multiple values ​​are emitted, they must be emitted as a single object. In my solution, the grandchild component emits the field name and value as a single object

grandson

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

The child object captures and re-eits, but first takes care to emit in the format expected by the parent component (it requires the entire data.primary object because that's what is set to 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);
}

Then the parent component will automatically receive and update the v-model data.primary object.

Or , I must mention that you can always use Pinia, the official state management library for Vue (to save some state in one component and read the same from any other component status). There's certainly a learning curve, but it's definitely worth learning and is designed to simplify this type of situation.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template