1

I have a modal component (child called modal_form.vue), which I call inside another component called index.vue

I would like to send a data variable from modal_form.vue to index.vue.

Can someone give me an example?

PS: modal_form contains an axios request, so I would like to send the response to index.vue

-- modal --

methods: {
    post() {
        getAPI.post('api/contact/', this.form)
        .then(response => (this.data = response.data))
        .then(() => (this.$emit('dataLoaded', this.data)))
        .catch(error => console.log(error.response))
    }
}

-- index --

<addContactModal v-if="openContactModal" :dataLoaded="refreshContactList"></addContactModal>

data: function(){
    return {
        openContactModal: true,
        contacts: []
    }
},
methods: {
    refreshContactList(data){
        this.contacts = JSON.parse( JSON.stringify( data ) )
    },
}
SoyChai
  • 320
  • 2
  • 11
Amine Bouhaddi
  • 554
  • 1
  • 7
  • 24

2 Answers2

3

If I understand your question correctly, you can use custom events to emit from a child component to a parent component in Vue.

https://v2.vuejs.org/v2/guide/components-custom-events.html

  1. The example below shows modal_form.vue containing a single button that upon click runs the method grabDataAndEmit.

  2. grabDataAndEmit makes an axios call using ES8 await syntax. The response is then stored in a variable and emitted to the parent via this code segment this.$emit( 'handle-form-data', response );.

  3. You can see the parent index.vue handles the event in this code segment @handle-form-data="handleFormDataCallback" where @handle-form-data is the event being listened for, and the value passed in is the handler/callback for that event.

Note: @ is just an abbreviated version of v-on:

<!-- modal_form.vue  -->
<template>
    <div>
        <div @click="grabDataAndEmit">button</div>
    </div>
</template>
<script>
import axios from 'axios';
export default {
    methods: {
        async grabDataAndEmit() {
            const response = await axios.get('/api/some/data');
            this.$emit( 'handle-form-data', response );
        }
    }
}
</script>

<!-- index.vue  -->
<template>
    <div>
        <!-- @handleFormData listens for "handleFormData" event to be $emit from the child.  -->
        <modal-form @handle-form-data="handleFormDataCallback" />
    </div>
</template>
<script>
import ModalForm from './modal_form.vue';
import axios from 'axios';
export default {
    components: { ModalForm },
    methods: {
        handleFormDataCallback( $event ) {
            // Do something with the $event data emitted.
            // $event in this case is equal to "response" from the child component.
            console.log( $event )
        }
    }
}
</script>

I hope this answer gives some help to your task at hand!

tony19
  • 125,647
  • 18
  • 229
  • 307
  • i used your code the only issue i still have is i try to refresh contacts variable, i did this.contacts = $event. but my v-for contact in contacts. not refreshed – Amine Bouhaddi Mar 23 '21 at 05:25
  • can you help me by updating my contacts variable , i already did, this.contacts = $event but my v-for not refreshed – Amine Bouhaddi Mar 23 '21 at 05:26
  • Hmm, i assume your $event is an array of objects? Maybe try doing `this.contacts = JSON.parse( JSON.stringify( $event ) )`. – Hicham Taha Mar 23 '21 at 05:40
  • same issue, i will update my question like that you can see my code ;) – Amine Bouhaddi Mar 23 '21 at 05:45
  • 1
    Fixed i had : instead of @ – Amine Bouhaddi Mar 23 '21 at 06:04
  • change `:dataLoaded` to `@dataLoaded`. Also you can handle the response in the first callback like so ``` getAPI.post('api/contact/', this.form) .then(response => { this.data = response.data; this.$emit( 'dataLoaded', response.data ) }) .catch(error => console.log(error.response)) ``` – Hicham Taha Mar 23 '21 at 06:04
1

If you do not want to use $emit and events there is a easier way with using Vuex. You should define your store like below guides and then use the mutations in your modal for setting new data and use store state in your another components like the index.vue that you said that you need the modal data . Here is the simplest store for just introduction to Vuex

npm install vuex --save

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

Now, you can access the state object as store.state, and trigger a state change with the store.commit method:

store.commit('increment')

console.log(store.state.count) // -> 1

In order to have an access to this.$store property in your Vue components, you need to provide the created store to Vue instance. Vuex has a mechanism to "inject" the store into all child components from the root component with the store option:

new Vue({
  el: '#app',
  store: store,
})
methods: {
  increment() {
    this.$store.commit('increment')
    console.log(this.$store.state.count)
  }
}

Note that if you have some Axios calls or async actions, you must use the actions object instead of mutations object for defining your functions . See below codes :

Your store schema :

state : {
    APIData : null
}

mutations : {
    setAPIData(state, newData) {
        state.APIData = newData
    }
}

In your Vue instance :

    methods: {
        post() {
            getAPI.post('api/contact/', this.form)
                .then(response => (this.data = response.data))
                .then(() => (this.$store.commit('setAPIData', this.data)))
                .catch(error => console.log(error.response))
        },
    },

In your another components you can access the new data with this.APIData as a computed property :

    computed : {
        APIData(){
            return this.$store.state.APIData
        }
    }
Momo
  • 794
  • 2
  • 7
  • 17