wonder95
wonder95

Reputation: 4245

Access input from dynamically loaded component in parent component in Vuejs

I've created a wizard-type flow in Vuejs using dynamically loaded components, where the Save button is in the parent component, and all of the text fields are in the loaded components. When the Save button is clicked, I need two things to happen:

1) The next component is loaded (currently working) 2) Get data from the text field(s) in the child component and write them to my Vuex store.

Here is the code for the parent component:

<template>
<div>
  <component :is="selectedComponent"></component>
  <span @click="changeComponent(selectedComponent)" class="action button save">Save</span>
</div>
</template>

<script>
import auth from '@/api//auth'
import Establishment from '@/onboarding/components/OnboardingEstablishment.vue'
import Account from '@/onboarding/components/OnboardingAccount.vue'
import Vendor from '@/onboarding/components/OnboardingVendors.vue'
import Location from '@/onboarding/components/OnboardingLocation.vue'
import Menu from '@/onboarding/components/OnboardingMenu.vue'

export default {
  data () {
    return {
      accountName: '',
      selectedComponent: 'account'
    }
  },
  components: {
    establishment: Establishment,
    account: Account,
    vendor: Vendor,
    location: Location,
    appMenu: Menu
  },
  methods: {
    changeComponent (current) {
      // Mapping object to map what the next component should be
      // when changing the dynamic component.
      const componentFlow = {
        account: 'establishment',
        establishment: 'location',
        location: 'vendor',
        vendor: 'appMenu'
      }
      // Get the name of the next component.
      var nextComponent = componentFlow[current]
      this.selectedComponent = nextComponent
      // We also need to update Vuex with the value from the text field.
    },
    updateState () {
      // Write the data from the element to the state.
    }
  },
  mounted () {
    // Get name of logged in user.
    auth.getAccountDetails()
      .then(response => {
        this.accountName = response.data.email
      })
  }
}
</script>

And as an example, here is OnboardingAccount.vue, one of the dynamically loaded components:

<template>
  <div>
    <h1>Hey {{ accountEmail }}</h1>
    <p>Let's start at the top.</p>
    <p>What do you want to name your Account?</p>
    <input :value="accountName" type="text" />
    <p>Note: Your account name is the top level of your management. Additional teams or establishments can be added under this account name.</p>
  </div>
</template>

<script>
import auth from '@/api/auth'
import Establishment from '@/onboarding/components/OnboardingEstablishment.vue'
import Account from '@/onboarding/components/OnboardingAccount.vue'
import Vendor from '@/onboarding/components/OnboardingVendors.vue'
import Location from '@/onboarding/components/OnboardingLocation.vue'

export default {
  data () {
    return {
      accountEmail: '',
      accountName: ''
    }
  },
  components: {
    establishment: Establishment,
    account: Account,
    vendor: Vendor,
    location: Location
  },
  mounted () {
    // Get name of logged in user.
    auth.getAccountDetails()
      .then(response => {
        this.accountEmail = response.data.email
      })
  }
}
</script>

What I need to do is somehow be able to access the value from the OnboardingAccount component when I click on the Save button in the parent Onboarding component so that I can write the date to the Vuex store, either in my changeComponent method, or elsewhere. In looking at the debugger, I don't see that value anywhere. How can I do what I need to do?

Upvotes: 1

Views: 1469

Answers (1)

8bit
8bit

Reputation: 597

I'd keep the state for every component in the parent component and pass them down as props, then on input events @change or @blur (or other) emit an event. This is is basically standard component communication, but since you're using dynamic components this gets a bit more tricky, in passing props and events.

To pass props to dynamic components somewhat cleanly you can use v-bind, but on a whole object instead of per variable/property, like here. The properties of this object can be objects containing props for each dynamic component, also this keeps the component tag clean. Then each dynamic child component can pick out the property from the props object that is meant for it, and use that data. For the event back from the children set a generic event on the component tag, name e.g. childEvent, to which each dynamic component would report a change of its state, as in it would return the changed props, which would update the state in the parent component. Saving should then be simple as all the data is in the parent component.

Side note: You're using dynamic components well but you can bring it to the next level if you utilize webpack's dynamic imports along with it, then you won't need to manually import each one and declare them as components. Take a look at this article.

Upvotes: 1

Related Questions