Search code examples
typescriptvue.jsvue-class-components

pass data from child component to parent component without any click in vue


I have a parent component ChangeInfo, inside it is a child component ShowWorkInfo. Inside the ShowWorkInfo component, I have several input forms to update the job information. I created a variable work_info which is an object and then used the v-model for the fields inside. But I don't know how to make the parent component get data from the child component. I don't have any button inside the child component, I'll try to handle the data from WorkInfo with a button in the parent. This is my code. Should I write directly into ChangeInfo without splitting it into a child component

ChangeInfo (parent component)

export default class ChangeInfo extends Vue {
  public isUpdated: boolean = false
  updateWorkInfo(workInfo: any) {
    if (
      workInfo.company == '' ||
      workInfo.department == '' ||
      workInfo.position == '' ||
      workInfo.postcode == '' ||
      workInfo.prefectures == '' ||
      workInfo.municipality == '' ||
      workInfo.address == '' ||
      workInfo.building == '' ||
      workInfo.phone_number == '' ||
      workInfo.url == ''
    ) {
      alert('完全な情報を入力してください')
      this.isUpdated = false
    } else {
      axios
        .put('https://609b82962b549f00176e394f.mockapi.io/work_info/1', {
          status: workInfo.status,
          company: workInfo.company,
          department: workInfo.department,
          position: workInfo.position,
          postcode: workInfo.postcode,
          prefectures: workInfo.prefectures,
          municipality: workInfo.municipality,
          address: workInfo.address,
          building: workInfo.building,
          phone_number: workInfo.phone_number,
          url: workInfo.url
        })
        .then(response => {
          workInfo = response.data
          console.log(workInfo)
          InformationModule.CHANGE_WORK_INFO(workInfo)
        })
        .catch(error => console.log(error))
      this.isUpdated = false
      workInfo.status = false
      workInfo.company = ''
      workInfo.department = ''
      workInfo.position = ''
      workInfo.postcode = ''
      workInfo.prefectures = ''
      workInfo.municipality = ''
      workInfo.address = ''
      workInfo.building = ''
      workInfo.phone_number = ''
      workInfo.url = ''
    }
  }

  updatePersonalInfo(personalInfo: any) {
    if (
      personalInfo.nearest_station == '' ||
      personalInfo.postcode == '' ||
      personalInfo.prefectures == '' ||
      personalInfo.municipality == '' ||
      personalInfo.address == '' ||
      personalInfo.building == '' ||
      personalInfo.phone_number == '' ||
      personalInfo.url == ''
    ) {
      alert('完全な情報を入力してください')
      this.isUpdated = false
    } else {
      axios
        .put('https://609b82962b549f00176e394f.mockapi.io/personal_info/1', {
          gender: personalInfo.gender,
          nearest_station: personalInfo.nearest_station,
          postcode: personalInfo.postcode,
          prefectures: personalInfo.prefectures,
          municipality: personalInfo.municipality,
          address: personalInfo.address,
          building: personalInfo.building,
          phone_number: personalInfo.phone_number,
          url: personalInfo.url
        })
        .then(response => {
          personalInfo = response.data
          console.log(personalInfo)
          InformationModule.CHANGE_PERSONAL_INFO(personalInfo)
        })
        .catch(error => console.log(error))
      this.isUpdated = false
      personalInfo.gender = false
      personalInfo.nearest_station
      personalInfo.postcode = ''
      personalInfo.prefectures = ''
      personalInfo.municipality = ''
      personalInfo.address = ''
      personalInfo.building = ''
      personalInfo.phone_number = ''
      personalInfo.url = ''
    }
  }

  triggerSubmit() {
    this.isUpdated = true
  }

I call two functions like this

<template>
  <div class="d-block">
    <ShowProfile />
    <ShowWorkInfo :isUpdated="isUpdated" @update-work-info="updateWorkInfo" />
    <ShowPersonalInfo
      :isUpdated="isUpdated"
      @update-personal-info="updatePersonalInfo"
    />
    <div class="w--27 mw-100 mx-auto my-9">
      <button
        @click="triggerSubmit"
        v-b-modal="'update-success'"
        class="btn btn-primary w-100"
      >
        {{ $t('common.btn.btn_update') }}
      </button>
    </div>
    <ModalUpdateSuccess />
  </div>
</template>

Solution

  • Well... there are multiple ways to do this:

    1. Quick and dirty: Pass another prop from the parent to component, this will be set to true when the common.btn.btn_update is pressed, in the updateWorkInfo function, you set up a watcher in the child component for this prop, and when this pro changes to true, you do an emit('submitted', data) and this will emit the data to your parent component and then just handle this event in the parent as a submit event. In action:

    //#region Parent script
    import {
      Component,
      Vue
    } from 'vue-property-decorator'
    import ShowWorkInfo from './Components/ShowWorkInfo.vue'
    import ModalUpdateSuccess from '@/components/Modal/ModalUpdateSuccess.vue'
    
    @Component({
      components: {
        ShowWorkInfo,
        ModalUpdateSuccess
      }
    })
    export default class ChangeInfo extends Vue {
      private isFormSubmitted: boolean = false;
      private validationMessage: string = '';
      updateWorkInfo(workInfo) {
        console.log('recieved workinfo: ', workInfo);
        const { isFormValid, validationMessage } = await validateForm(workInfo);// function that validates your data, and returns a message and a boolean.
        if(!isFormValid) { //if form is not valid, you have to set the variable that triggers 
                           //the submit to false so the child component can set  it to true and trigger submit once again
          this.isFormSubmitted = false;
          this.validationMessage = validationMessage;
          return;
        } 
        //...data handling logic and stuff... you can make the PUT request here, or anuy other data related action
      }
      triggerSubmit() {
        //this triggers the watch in the child component
        this.isFormSubmitted = true;
      }
    }
    //# endRegion Parent script
    
    //# region Child script
    @Component({
      components: {
        Title
      }
      //the prop
      props: {
        isFormSubmitted: {
          default: false,
          type: boolean,
        },
      }
    })
    export default class ShowWorkInfo extends Vue {
      private showWorkInfo: boolean = false
      private workInfo: any = { ...
      }
      //watch for change (you can also put it in a computed if it does not trigger change)
      watch: {
        isFormSubmitted: function(newValue) {
          if (newValue) {
            console.log('form-submitted', this.workInfo)
            emit('form-submitted', this.workInfo);
          }
        }
      }
    }
    //#endRegion Child script

    Now I don't really like this approach... but it works, just how you wanted it to!

    1. Traditionally: You would have the submit button in the child component and emit a submitted event, together with the data, way nicer, and more reusable approach:

    #region Parent script
    @Component({
      components: {
        ShowWorkInfo,
        ModalUpdateSuccess
      }
    })
    export default class ChangeInfo extends Vue {
      updateWorkInfo(workInfo) {
        console.log('recieved workinfo: ', workInfo);
        //...data handling logic and stuf...
      }
    }
    
    
    ShowWorkInfo (child component)
    #endRegion Parent script
    
    #region Child script
    @Component({
      components: {
        Title
      }
    })
    export default class ShowWorkInfo extends Vue {
      private showWorkInfo: boolean = false
      private workInfo: any = { ... }
      //watch for change (you can also put it in a computed if it does not trigger change)
      submitForm() {
        console.log('submit form: ', this.workInfo);
        //you could also validate the form at this point and dont emit unti the data is valid
        this.$emit('form-submitted', this.workInfo);
      }
    }
    #endRegion Child script
    <!-- parent template -->
    <template>
      <div class="d-block">
        <ShowWorkInfo @form-submitted="updateWorkInfo"/>
        <ModalUpdateSuccess />
      </div>
    </template>
    <!-- parent template end -->
    
    <!-- child template -->
    <template>
      <div class="form">
      <!-- your form fields -->
       <div class="w--27 mw-100 mx-auto my-9">
          <button
            @click="submitForm"
            v-b-modal="'update-success'"
            class="btn btn-primary w-100"
          >
            {{ $t('common.btn.btn_update') }}
          </button>
        </div>
      </div>
    </template>
    <!-- child template end -->

    NOTE1: Apologies if I misused these variables and the this, since i am not familiar with this version of Vue and typescript, and tbh i have not touched vue.2 in like a year, but the concepts remain the same, essentially i just wanted to show you the methods of how to use the features vue gives, the above code is far from perfect but i think it illustrates the uses and answers you question.

    NOTE2: There are more ways to do this, if you are interested in them please tell me. The above-mentioned two ways are the most common I have seen

    UPDATE: Form validation in the first block. Try it, this is an overall form validation. DISCLAIMER: If you want to do field by field validation, that would be real hard, and would advise you to move the submit logic to your child component.