I'm creating an instant messenger as a Vue.js learning exercise and I am encountering the confounding error wherein arrays and objects passed as props for slotted child components to access are becoming strings, and producing errors of the like:
[Vue warn]: Invalid prop: type check failed for prop "messageGroup". Expected Array, got String with value "[object Object],[object Object],[object Object],[object Object]".
and
[Vue warn]: Invalid prop: type check failed for prop "message". Expected Object, got String with value "[object Object]".
Passing primitive types as props for child components works as expected. Passing objects and arrays, using either :prop-name or v-bind:prop-name results in strings.
How may I resolve this or how is my understanding of the proper way to do this lacking?
Here is the full code for the components and how they are registered:
Messenger:
<template>
<section id="messenger">
<header>
<h1>Test Conversation Window</h1>
</header>
<aside class="control-menu">
Control Menu
<hr>
</aside>
<!-- The aggrieving section -->
<main v-if="messageGroups.length" >
<vue-message-group
v-for="(messageGroup, i) of messageGroups"
:key="i"
v-bind:message-group="messageGroup"
:username="messageGroup[0].username" >
<vue-message
v-for="message of messageGroup" :key="message.id"
v-bind:message="message" >
</vue-message>
</vue-message-group>
</main>
<!-- -- -->
<aside class="participants">
Participants
<hr>
<ul v-if="participants.length">
<li v-for="(user, i) in participants" :key="i">
<img v-bind:src="user.avatar" v-bind:alt="user.username">
<span>{{ user.username }}</span>
</li>
</ul>
</aside>
</section>
</template>
<script>
export default {
data() {
return {
participants: [],
messageGroups: null,
}
},
created() {
this.getParticipants( [ 'john', 'karen' ] );
this.messageGroups = this.groupMessages( this.testMessages() );
},
methods: {
getParticipants: async function( users ) {
this.url = 'http://*.*.*.*/api/user/';
this.participants = await Promise.all( users.map(
async participant =>
await this.getUserFromAPI( participant )
) );
},
getUserFromAPI: async function( username )
{
return await fetch( this.url + username )
.then( data => data.json() )
.then( data => data.data );
},
groupMessages: function( messages ) {
let lastUsername = null;
let groups = new Array();
let group = null;
for ( const message of messages )
{
if ( message.username !== lastUsername )
{
group = new Array();
group.push( message );
groups.push( group );
lastUsername = message.username;
}
else
{
group.push( message );
}
}
return groups;
},
testMessages: function() {
return [
{
id: 0,
username: 'john',
message: 'TEST 1 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 1,
username: 'john',
message: 'TEST 2 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 2,
username: 'john',
message: 'TEST 3 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 3,
username: 'karen >:[',
message: 'TEST 4 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 4,
username: 'karen >:[',
message: 'TEST 5 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 5,
username: 'john',
message: 'TEST 6 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 6,
username: 'karen >:[',
message: 'TEST 7 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 7,
username: 'karen >:[',
message: 'TEST 8 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 8,
username: 'karen >:[',
message: 'TEST 9 message content.',
timestamp: '2020-05-20T23:09:13',
},
{
id: 9,
username: 'karen >:[',
message: 'TEST 10 message content.',
timestamp: '2020-05-20T23:09:13',
},
];
},
},
}
</script>
Message-group:
<template>
<figure class="message">
<main>
<span>{{ username }}</span>
<slot></slot>
</main>
</figure>
</template>
<script>
export default {
props: {
username: String,
messageGroup: Array,
},
}
</script>
Message:
<template>
<div class="message">
<span class="line">{{ message.message }}</span>
<div class="timestamp">{{ message.timestamp }}</div>
</div>
</template>
<script>
export default {
props: {
message: Object,
},
}
</script>
App.js:
import Vue from 'vue'
import vueCustomElement from 'vue-custom-element'
/*
* See: https://github.com/karol-f/vue-custom-element
*/
Vue.use(vueCustomElement);
Vue.config.ignoredElements = [
'vue-messenger',
'vue-message-group',
'vue-message',
];
import VueMessenger from './components/VueMessenger.vue'
const messengerStyles = require( '!css-loader!sass-loader!./components/css/VueMessenger.scss' );
Vue.customElement(
'vue-messenger',
VueMessenger,
{
shadow: true,
shadowCss: messengerStyles.toString(),
}
);
import VueMessageGroup from './components/VueMessageGroup.vue'
const messageGroupStyles = require( '!css-loader!sass-loader!./components/css/VueMessageGroup.scss' );
Vue.customElement(
'vue-message-group',
VueMessageGroup,
{
shadow: true,
shadowCss: messageGroupStyles.toString(),
}
);
import VueMessage from './components/VueMessage.vue'
const messageStyles = require( '!css-loader!sass-loader!./components/css/VueMessage.scss' );
Vue.customElement(
'vue-message',
VueMessage,
{
shadow: true,
shadowCss: messageStyles.toString(),
}
);
The problem stems from the fact that I am registering the Vue components as custom elements with the browser; the browser requires all element attributes to be strings, and therefor coerces objects and arrays.
The most simple work-around seems to be passing object properties instead of objects themselves.
I am experimenting with passing json in the :props.