Passing variables from within VueJS components

Viewed 347

I am new to VueJs and Laravel. I am trying to build a chat app following this tutorial, and my problem is that I am not able to pass a CONTACT variable (object type) from ChatApp.vue to Conversation.vue. The console gives this error

[Vue warn]: Invalid prop: type check failed for prop "contact". Expected Object, got Null 
found in ---> <MessageFeed> at resources/js/officer/components/MessageFeed.vue
   <Conversation> at resources/js/officer/components/Conversation.vue
     <ChatApp> at resources/js/officer/components/ChatApp.vue
       <Root>

first the variable has to pass through ChatApp to Conversation and then to MessageFeed. I tried printing it on console, in ChatApp.vue, it printed the variable's property i.e. name but when I try to print it in Conversation.vue, it says it is null and also gives the above error.

please see the code below:

ChatApp.vue

<template>
<div class="chat-app">
    <Conversation :contact="selectedContact" :messages="messages" @new="saveNewMessage"/>
    <ContactsList :contacts="contacts" @selected="startConversationWith"/>
</div>
</template>
<script>
import Conversation from "./Conversation";
import ContactsList from "./ContactsList";
const axios = require('axios');
export default {
    props: {
        user: {
            type: Object,
            required: true
        }
    },
    data() {
        return {
            selectedContact: null,
            messages: [],
            contacts: []
        }
    },
    mounted() {
        Echo.private(`messages.${this.user.id}`)
            .listen('NewMessage', (e) => {
                this.handleIncoming(e.message);
            });
        console.log(this.user);
        axios.get('/officer/contacts')
            .then((response) => {
                this.contacts = response.data;
                console.log("chatapp1: " + this.contacts[0].name);
            })
            .catch((error) => {
                // handle error
                console.log(error);
            });
    },
    methods: {
        startConversationWith(contact) {
            axios.get(`/officer/conversation/${contact.id}`)
                .then((response) => {
                    this.message = response.data;
                    this.selectedContact = contact;
                    console.log("chatapp: " + this.selectedContact.name);
                });
        },
        saveNewMessage(message) {
            this.messages.push(message);
        },
        handleIncoming(message) {
            if (this.selectedContact && message.from === this.selectedContact.id) {
                this.saveNewMessage(message);
                return;
            }
            this.updateUnreadCount(message.from_contact, false);
        },
        updateUnreadCount(contact, reset) {
            this.contacts = this.contacts.map((single) => {
                if (single.id !== contact.id) {
                    return single;
                }
                if (reset)
                    single.unread = 0;
                else
                    single.unread += 1;
                return single;
            })
        }
    },
    components: {Conversation, ContactsList}
}

Conversation.vue

<template>
<div class="conversation">
    <h1>{{ contact ? contact.name : 'Select a Contact' }}</h1>
    <MessagesFeed :contact="contact" :messages="messages"/>
    <MessageComposer @send="sendMessage"/>
</div>
</template>
<script>
import MessagesFeed from './MessageFeed';
import MessageComposer from './MessageComposer';
export default {
    props: {
        contact: {
            type: Object,
            default: null
        },
        messages: {
            type: Array,
            default: []
        }
    },
    mounted() {
        console.log("conversation: "+this.contact);
    },
    methods: {
        sendMessage(text) {
            if (!this.contact) {
                return;
            }
            console.log(text);
            axios.post('/conversation/send', {
                contact_id: this.contact.id,
                text: text
            }).then((response) => {
                this.$emit('new', response.data);
            })
        }
    },
    components: {MessagesFeed, MessageComposer}
}

MessageFeed.vue

<template>
<div class="feed" ref="feed">
    <ul v-if="contact">
        <li v-for="message in messages" :class="`message${message.to == contact.id ? 'sent' : 'received'}`"
            :key="message.id">
            <div class="text">
                {{message.text}}
            </div>
        </li>
    </ul>
</div>
</template>
<script>
export default {
    props: {
        contact: {
            type: Object,
            required: true
        },
        messages: {
            type: Array,
            required: true
        }
    },
    methods: {
        scrollToBottom() {
            setTimeout(() => {
                this.$refs.feed.scrollTop = this.$refs.feed.scrollHeight - this.$refs.feed.clientHeight;
            }, 1);
        }
    },
    watch: {
        contact(contact) {
            this.scrollToBottom();
        },
        messages(messages) {
            this.scrollToBottom();
        }
    }
}

please, let me know what am I missing here.

2 Answers

Because you konfigurated it to be an Object but you declared it on the initial state as null

  selectedContact: null

That should be an object

It should look something like this i guess:

  selectedContact: {
       id:0
  }

I fixed the issue. This was due to axios.get() response thing. It was fetching data from database after a couple of minutes and the Conversation.vue and ContactList.vue were mounting immediately so they were not getting data. With the help of my friend, it was fixed by loading the component only when axios have got all the contactlist from database and the very first contact have been passed to the Conversation.vue component i.e using v-if property of component.

Related