Home » vue » Vue 2 Emit selected data back to parent component

Vue 2 Emit selected data back to parent component

Posted by: admin November 26, 2021 Leave a comment

Questions:

Struggling to sort out how to get a selected value from a Typeahead component to pass back to the parent component. I’m allowing the user to search from a variety of data to link a record to a post. Once the user clicks one of the typeahead drop-down records, I pass the item to the sendlink method – I’ve checked that the data passes ok. When I do the emit using the selected-link event, I’m not getting the data in the parent component.

PostList.vue

<template>
    <div>
        <div v-if='posts.length === 0' class="header">There are no posts yet!</div>
            <form action="#" @submit.prevent="createPost()" class="publisher bt-1 border-fade bg-white" autocomplete="off">
                <div class="input-group">
                    <input v-model="post.content" type="text" name="content" class="form-control publisher-input" placeholder="What's the lastest?" autofocus>
                    <span class="input-group-btn">
                        <button type="submit" class="btn btn-primary">Post</button>
                    </span>
                </div>
                <span class="publisher-btn file-group">
                  <i class="fa fa-camera file-browser"></i>
                  <input type="file">
                </span>
            </form>
            <div @click="doit" v-on:selected-link="onSelectedLink">{{ modellink.name }}</div>
             <typeahead
                source="/api/typeahead"
                placeholder="Link Post to Trip, Business, etc"
                filter-key="title"
                :start-at="3">
             </typeahead> 
            <post v-for="post in posts"
                     :key="post.id"
                     :post="post"
                     @post-deleted="deletePost($event)">
            </post>
    </div>
</template>
<script>
    var axios = require("axios");
    import post from './PostItem.vue';
    import typeahead from './Typeahead.vue';
    export default {
        components: {
            post, 
            typeahead
        },
        props: ['postableId', 'postableType', 'model'],
        data: function() {
            return {
                modellink: {
                   "name": "n/a",
                   "description": "",
                   "id": null,
                   "model": "n/a"
                },
                post: {
                        id: 1,
                        content: "",
                        edited: false,
                        created_at: new Date().toLocaleString(),
                        user: {
                            id: 1,
                            name: '',
                        }
                    },
                posts: [
                    {
                        id: 1,
                        content: "",
                        edited: false,
                        created_at: new Date().toLocaleString(),
                        user: {
                            id: 1,
                            name: '',
                        }
                    }
                ]
            };
        },        
        created() {
            this.fetchPostsList();
        },

        methods: {
            onSelectedLink: function (talink) {
                alert(JSON.stringify(talink, null, 4));
                this.link = talink
            },
            doit() {
                alert(JSON.stringify(this.modellink, null, 4));
            },
            fetchPostsList() {
                if( this.postableId ) {
                    axios.get('/api/' + this.postableType + '/' + this.postableId + '/posts').then((res) => {
                        this.posts = res.data;
                    });
                } else {
                    axios.get('/api/post').then((res) => {
                        //alert(JSON.stringify(res.data[0], null, 4));
                        this.posts = res.data;
                    });
                }
            },

            createPost() {
                axios.post('api/post', {content: this.post.content, user_id: Laravel.userId, vessel_id: Laravel.vesselId })
                    .then((res) => {
                        this.post.content = '';
                        // this.post.user_id = Laravel.userId;
                        // this.task.statuscolor = '#ff0000';
                        this.edit = false;
                        this.fetchPostsList();
                    })
                    .catch((err) => console.error(err));
            },
            deletePost(post) {
                axios.delete('api/post/' + post.id)
                    .then((res) => {
                        this.fetchPostsList()
                    })
                    .catch((err) => console.error(err));
            },
        }
    }
</script>

Typeahead.vue

<template>
    <div>
        <input 
            v-model="query"
            @blur="reset"
            type="text" 
            class="SearchInput" 
            :placeholder="placeholder">     
            <transition-group name="fade" tag="ul" class="Results">
                <li v-for="item in items" :key="item.id">
                    <span @click="sendlink(item)">
                        <strong>{{ item.name }}</strong> - <small>{{ item.model }}</small><br>
                        <small>{{ item.description }}</small>
                    </span>
                </li>
            </transition-group>
             <p v-show="isEmpty">Sorry, but we can't find any match for given term :( </p>
    </div>
</template>

<script>
    var axios = require("axios");
    export default {
        name: 'Typeahead',
        props: {
            modellink: {
                type: Object,
                required: false
            },
            source: {
                type: [String, Array],
                required: true
            },
            filterKey: {
                type: String,
                required: true
            },
            startAt: {
                type: Number,
                default: 3
            },
            placeholder: {
                type: String,
                default: ''
            }
       },
        data() {
            return {
                items: [],
                query: '',
                taitem: ''
            }
        },
        computed: {
            lookup() {
                if(this.query.length >= this.startAt) {
                    axios.get(this.source + '/' + this.query).then((res) => {
                        this.items = res.data;
                        return res.data;
                    });
                }
            },
            isEmpty() {
                if( typeof this.lookup === 'undefined'  ) {
                    return false
                } else {
                    return this.lookup.length < 1
                }
            }
        },
        methods: {
            sendlink: function (taitem) {
              this.$emit('selected-link', taitem);
            },
            reset() {
                this.query = ''
            }            
        }
    }
</script>
Answers:

In your PostList.vue, move the v-on:selected-link="onSelectedLink" from the div to typeahead like below. When emitting an event from child to parent, the listener on the parent needs to be on the child component tag for it to work.

 <div @click="doit">{{ modellink.name }}</div>
         <typeahead
            source="/api/typeahead"
            placeholder="Link Post to Trip, Business, etc"
            filter-key="title"
            :start-at="3"
            v-on:selected-link="onSelectedLink">
         </typeahead>