5

I would like to retrieve all input values from my child components (client and advice, seen below), but not sure how to proceed.

client.vue

<template>
    <div id="client">
        <input type="text" v-model="client.name" />
        <input type="text" v-model="client.code" />
    </div>
</template>

<script>
    export default {
        data() {
            return {
                client: {
                    name: '',
                    code: '',
                }
            }
        }
    }
</script>

advice.vue

<template>
    <div id="advice">
        <input type="text" v-model="advice.foo" />
        <input type="text" v-model="advice.bar" />
        <div v-for="index in 2" :key="index">
            <input type="text" v-model="advice.amount[index]" />
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                advice: {
                    foo: '',
                    bar: '',
                    amount:[]
                }
            }
        }
    }
</script>

Each component has more fields than the above example.

My home page (parent) looks as simple as:

<template>
    <form id="app" @submit="printForm">
        <clientInfo />
        <advice />
        <input type="submit" value="Print" class="btn" />
    </form>
</template>

<script>
    import clientInfo from "@/components/clientInfo.vue";
    import advice from "@/components/advice.vue";

    export default {
        components: {
            clientInfo,
            advice
        },
        methods: {
            printForm() {}
        }
    }
</script>

My first idea was to $emit, but not sure how to do that efficiently with more than 20 fields without attaching a @emitMethod="parentEmitMethod" to every single field.

My second idea was to have a Vuex store (as seen below), but I don't know how to save all the states at once and not sure if I should.

new Vuex.Store({
    state: {
        client: {
            name:'',
            code:''
        },
        advice: {
            foo:'',
            bar:'',
            amount:[]
        }
    }
})

3 Answers 3

4

You could use FormData to get the values of the form's <input>s or <textarea>s that have a name attribute (anonymous ones are ignored). This works even if the form has nested Vue components that contain <input>s.

export default {
  methods: {
    printForm(e) {
      const form = e.target
      const formData = new FormData(form) // get all named inputs in form
      for (const [inputName, value] of formData) {
        console.log({ inputName, value })
      }
    }
  }
}

demo

Sign up to request clarification or add additional context in comments.

Comments

3

You could use v-model with your custom components. Let's say you want to use them like this:

<advice v-model="adviceData"/>

For this, you would need to watch for value changes on your input elements inside your advice component and then emit an input event with the values. This will update the adviceData binded property. One generic way to do this could be including a watcher inside your advice component, like this:

export default {
  data() {
    return {
      advice: {
        foo: '',
        bar: '',
        amount:[]
      }
    }
  },
  watch: {
    advice: {
      handler: function(newValue, oldValue) {
        this.$emit('input', newValue);
      },
      deep: true,
    }
  },
}

This way you will not have to add a handler for each input field. The deep option must be included if we need to detect changes on nested data in the advice object.

4 Comments

where would the watcher be? inside the component?
@Jonathan in each custom component you want to use v-model with.
can you please create an example, I'm not quite understanding how to make it work
@Jonathan I've updated my answer with a more complete example. For more information on vue watchers: it.vuejs.org/v2/guide/computed.html#Watchers
1

I think you can achieve what you want is when the user writes something using@change this will trigger a method when the input value is changed, you could use a button instead or anything you want, like this:

The child component

<input type="text" v-model="advice.amount[index]" @change="emitCurrStateToParent ()"/>

You gotta add @change="emitCurrStateToParent ()" in every input you have.

emitCurrStateToParent () {
    this.$emit("emitCurrStateToParent", this.advice)
}

Then in you parent component

<child-component v-on:emitCurrStateToParent="reciveDataFromChild ($event)"></child-component>
reciveDataFromChild (recivedData) {
    // Do something with the data
}

I would use a button instead of @change, like a "Save" button idk, the same goes for vuex, you can use the @change event

saveDataAdviceInStore () {
    this.$store.commit("saveAdvice", this.advice)
}

Then in the store

mutations: {
    saveAdvice (state, advice) {
        state.advice = advice
    }
}    

1 Comment

that's what I mentioned that I don't want to do in the first place

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.