Closed cedric5 closed 2 months ago
Still experiencing this issue. Is this project still maintained?
Still experiencing this issue. Is this project still maintained?
Where is this.fetchCandidates()
located within the file? Can you please post a the full component or link a repo?
A while ago I changed something that resolved this issue, I could not pinpoint what it was. But today I fell upon the same issue again and cant figure it out what is wrong exactly
So now I have a component where everything is working as expected:
.row(style="height:100%")
.col-md-9(style="width:100%; height:92%;")
q-scroll-area(ref='scrollArea' style='height: 100%; max-width: 100%;')
div(v-if="messages.length > 0" v-for="message in messages" style='width: 100%; padding-left:24px; padding-right:24px;')
q-chat-message(v-if="message.originatorRole === 'bot' || message.originatorRole === 'user'" name='Agent' avatar='https://recrubo.com/wp-content/themes/recrubo/dist/images/recrubo-logo.png' :text="[message.fallback]" sent :stamp='message.createdAt')
q-chat-message(v-if="message.originatorRole === 'external'" :name='conversation.candidate.name' avatar='https://icon-library.com/images/avatar-icon-png/avatar-icon-png-11.jpg' :text="[message.fallback]" :stamp='message.createdAt')
div(v-else)
.text-center.absolute-center.text-body1 Please select a conversation
.row(style="width: 100%;")
q-input(@keyup.enter="sendMessage()" autogrow style="padding-left:8px; padding-right:12px; width:100%;" bg-color="white" rounded outlined v-model='message' label='Type your message..')
template(v-slot:append)
q-btn(@click="sendMessage()" round dense flat icon='send')
template(v-slot:after)
</template>
<script>
import { defineComponent } from 'vue'
import _ from 'lodash'
import backendApi from '@/api/backend'
import { mapGetters } from 'vuex'
export default defineComponent({
channels: {
ChatChannel: {
received (data) {
const message = data.attributes
message.id = data.id
this.messages.push(message)
this.messages = _.orderBy(this.messages, ['createdAt'], ['ASC'])
this.scrollToBottom()
const conversation = _.cloneDeep(this.conversation)
conversation.unreadMessages = false
this.$emit('updateConversation', conversation)
backendApi.update('conversation', this.conversation).then(() => {}) // Updating unread status in backend
}
}
},
props: {
conversation: Object
},
data () {
return {
messages: [],
message: ''
}
},
computed: {
...mapGetters('user', ['currentOrg'])
},
methods: {
fetchMessages () {
this.$q.loading.show()
backendApi.find('flow-thread', this.conversation.flowThread.id, { include: ['messages'] })
.then((data) => {
this.messages = []
this.messages.push(data.data.messages)
this.messages = _.flatten(this.messages)
this.messages = _.orderBy(this.messages, ['createdAt'], ['ASC'])
}).finally(() => {
this.scrollToBottom()
})
},
sendMessage () {
backendApi.request(process.env.API_URL + '/messaging/send-message/' + this.conversation.flowThread.id, 'POST', {}, { message: this.message })
.then((data) => {
this.message = ''
this.messages.push(data.data)
this.messages = _.flatten(this.messages)
this.messages = _.orderBy(this.messages, ['createdAt'], ['ASC'])
}).finally(() => {
this.scrollToBottom()
})
},
scrollToBottom () {
setTimeout(() => {
this.$refs.scrollArea.setScrollPercentage('vertical', 1)
this.$q.loading.hide()
}, 300)
}
},
mounted () {
this.$cable.subscribe({
channel: 'ChatChannel',
thread_id: this.conversation.flowThread.id
})
},
watch: {
conversation: {
immediate: true,
handler () {
this.fetchMessages()
}
}
}
})
</script>
And the other component that I was working on today where I am experiencing the same issue as described before.
.row
.col-md-12(style="height: 50%;")
q-infinite-scroll(@load='onload' :offset='500')
div(v-if="conversations.length > 0" v-for='conversation in conversations' :key='conversation.id')
q-intersection.example-item(transition='scale')
q-item(clickable v-ripple @click="setSelectedConversation(conversation)")
q-item-section(avatar)
q-avatar(color='primary' text-color='white')
| {{ avatarInitials(conversation.candidate) }}
q-item-section
q-item-label {{ conversation.candidate.fullName }}
q-item-label(caption lines='1') {{ conversation.flowThread.channel }}
q-item-section(side v-if="conversation.unreadMessages")
q-icon(name='fiber_manual_record' size='xs' color='green')
div(v-if="allConversationsLoaded")
q-separator(style="margin-top:12px" inset)
.text-subtitle1.text-center(style="margin-top:12px; color: #909396; padding-bottom:24px;") All conversations loaded
template(v-if="!allConversationsLoaded" v-slot:loading)
.row.justify-center.q-my-md
q-spinner-dots(color='primary' size='40px')
</template>
<script>
import { defineComponent } from 'vue'
import _ from 'lodash'
import backendApi from '@/api/backend'
import { mapGetters } from 'vuex'
export default defineComponent({
channels: {
ConversationChannel: {
received (data) {
const conversation = data.attributes
conversation.id = data.id
const index = _.findIndex(this.conversations, { id: conversation.id })
this.conversations[index] = conversation
}
}
},
data () {
return {
conversations: [],
conversationPage: 1,
maxConversationPage: 0
}
},
computed: {
...mapGetters('user', ['currentUser']),
allConversationsLoaded () {
return this.conversationPage > this.maxConversationPage
}
},
methods: {
avatarInitials (candidate) {
return candidate.name.substr(0, 1).toUpperCase()
},
onload (index, done) {
if (!this.allConversationsLoaded) {
setTimeout(() => {
this.conversationPage += 1
backendApi.findAll('conversations', { page: this.conversationPage, size: 30, include: ['user', 'candidate', 'flowThread'] })
.then((data) => {
this.maxConversationPage = data.meta.pageCount
this.conversations = _.concat(this.conversations, data.data)
})
.finally(() => {
done()
})
}, 2000)
} else {
done()
}
},
setSelectedConversation (conversation) {
conversation.unreadMessages = false
backendApi.update('conversation', conversation).then(() => {}) // Updating unread status in backend
this.$emit('selectedConversation', conversation)
},
fetchConversations () {
backendApi.findAll('conversations', { page: this.conversationPage, size: 30, include: ['user', 'candidate', 'flowThread'] })
.then((data) => {
this.maxConversationPage = data.meta.pageCount
this.conversations = _.concat(this.conversations, data.data)
})
.finally(() => {
this.fetching = false
})
}
},
mounted () {
this.fetchConversations()
this.$cable.subscribe({
channel: 'ConversationChannel',
organization_id: this.currentUser.organization.id
})
}
})
</script>
Which results in a Uncaught TypeError: this.conversations is undefined
Neither calling a method as in the issue description is working
I have tried recompiling multiple times.
After some hours of debugging I found the following:
I made a simple test component named Test
without all the clutter.
<template lang="pug">
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
channels: {
ConversationChannel: {
connected () {
console.log('connected')
},
received (data) {
this.conversations.push(data)
console.log(this.conversations)
}
}
},
data () {
return {
conversations: []
}
},
mounted () {
this.$cable.subscribe({
channel: 'ConversationChannel',
organization_id: '2f406421-ca98-43f7-9c6a-d6183b935afa'
})
}
})
</script>
I have a page where I initialize this component
<template lang="pug">
div
q-splitter(v-model='splitterModel' style='height: 92.5vh;')
template(v-slot:before)
conversations(@selectedConversation="setSelectedConversation($event)")
template(v-slot:after)
q-splitter(v-model='splitterModel2')
template(v-slot:before)
test(v-if="selectedConversation")
template(v-slot:after)
.col-md-3
candidate-card(v-if="selectedConversation" :basicMode="true" :candidate="selectedConversation.candidate")
</template>
Whener I use a v-for to decide to render the component it all works as expected. How ever if I remove the v-for the above mentioned errors appear.
test(v-if="selectedConversation")
works
test()
does not work
edit:
Using test(v-if="selectedConversation")
works but if you afterwards subscribe to another channel in another component but within the same parent component the errors recur again. It looks like the context of the channels {} block seems to be changing and can not handle multiple subscription within the same (parent) component.
I will try to create a demo project one of these days.
" It looks like the context of the channels {} block seems to be changing and can not handle multiple subscription within the same (parent) component."
Can confirm! If the subscription is in a parent component, the child components with subscriptions have the context of the parent.
The problem is, that actioncable-vue uses context._uid
in some parts of the code (e.g. src/cable.js#L231).
Vue 3 don't uses this._uid
anymore. For the options API it can be retrieved with this.$.uid
. For composition API the method call getCurrentInstance().uid
should be used.
Here two workarounds for options API and composition API.
/* cable.js: Small constant export to use it as mixin. */
export const ActionCable = {
beforeCreate() {
this._uid = this.$.uid
},
}
/* In the component include cable.js as mixin. */
import { ActionCable } from 'cable.js'
export default {
...
mixins: [ActionCable],
...
}
import { getCurrentInstance } from 'vue'
...
app.mixin({
beforeCreate() {
this._uid = getCurrentInstance().uid
},
})
Related to #49: created
hook want work, so we need to use beforeCreate
hook.
@mclintprojects
Describe the bug I'm trying to call a method within my component when ever I receive a websocket call.
However method or data calls within one of the channel methods (connected, received etc.) will throw an error.
How do I access my the data and methods of my component?
To Reproduce Like the documentation I have declared my channel like this in the component.
Expected behaviour
Screenshots Whenever the the websocket update is triggerd
Plugin version (please complete the following information): In my yarn.lock
Additional context I am using quasar