I'm using a Vue.js computed property but am running into an issue: The computed method IS being called at the correct times, but the value returned by the computed method is being ignored!
My method
computed: {
filteredClasses() {
let classes = this.project.classes
const ret = classes && classes.map(klass => {
const klassRet = Object.assign({}, klass)
klassRet.methods = klass.methods.filter(meth => this.isFiltered(meth, klass))
return klassRet
})
console.log(JSON.stringify(ret))
return ret
}
}
The values printed out by the console.log
statement are correct, but when I use filteredClasses
in template, it just uses the first cached value and never updates the template. This is confirmed by Vue chrome devtools (filteredClasses
never changes after the initial caching).
Could anyone give me some info as to why this is happening?
Project.vue
<template>
<div>
<div class="card light-blue white-text">
<div class="card-content row">
<div class="col s4 input-field-white inline">
<input type="text" v-model="filter.name" id="filter-name" />
<label for="filter-name">Name</label>
</div>
<div class="col s2 input-field-white inline">
<input type="text" v-model="filter.status" id="filter-status" />
<label for="filter-status">Status (PASS or FAIL)</label>
</div>
<div class="col s2 input-field-white inline">
<input
type="text"
v-model="filter.apkVersion"
id="filter-apkVersion"
/>
<label for="filter-apkVersion">APK Version</label>
</div>
<div class="col s4 input-field-white inline">
<input
type="text"
v-model="filter.executionStatus"
id="filter-executionStatus"
/>
<label for="filter-executionStatus"
>Execution Status (RUNNING, QUEUED, or IDLE)</label
>
</div>
</div>
</div>
<div v-for="(klass, classIndex) in filteredClasses">
<ClassView :klass-raw="klass" />
</div>
</div>
</template>
<script>
import ClassView from './ClassView.vue'
export default {
name: 'ProjectView',
props: {
projectId: {
type: String,
default () {
return this.$route.params.id
}
}
},
data () {
return {
project: {},
filter: {
name: '',
status: '',
apkVersion: '',
executionStatus: ''
}
}
},
async created () {
// Get initial data
const res = await this.$lokka.query(`{
project(id: "${this.projectId}") {
name
classes {
name
methods {
id
name
reports
executionStatus
}
}
}
}`)
// Augment this data with latestReport and expanded
const reportPromises = []
const reportMeta = []
for (let i = 0; i < res.project.classes.length; ++i) {
const klass = res.project.classes[i]
for (let j = 0; j < klass.methods.length; ++j) {
res.project.classes[i].methods[j].expanded = false
const meth = klass.methods[j]
if (meth.reports && meth.reports.length) {
reportPromises.push(
this.$lokka
.query(
`{
report(id: "${
meth.reports[meth.reports.length - 1]
}") {
id
status
apkVersion
steps {
status platform message time
}
}
}`
)
.then(res => res.report)
)
reportMeta.push({
classIndex: i,
methodIndex: j
})
}
}
}
// Send all report requests in parallel
const reports = await Promise.all(reportPromises)
for (let i = 0; i < reports.length; ++i) {
const { classIndex, methodIndex } = reportMeta[i]
res.project.classes[classIndex].methods[methodIndex].latestReport =
reports[i]
}
this.project = res.project
// Establish WebSocket connection and set up event handlers
this.registerExecutorSocket()
},
computed: {
filteredClasses () {
let classes = this.project.classes
const ret =
classes &&
classes.map(klass => {
const klassRet = Object.assign({}, klass)
klassRet.methods = klass.methods.filter(meth =>
this.isFiltered(meth, klass)
)
return klassRet
})
console.log(JSON.stringify(ret))
return ret
}
},
methods: {
isFiltered (method, klass) {
const nameFilter = this.testFilter(
this.filter.name,
klass.name + '.' + method.name
)
const statusFilter = this.testFilter(
this.filter.status,
method.latestReport && method.latestReport.status
)
const apkVersionFilter = this.testFilter(
this.filter.apkVersion,
method.latestReport && method.latestReport.apkVersion
)
const executionStatusFilter = this.testFilter(
this.filter.executionStatus,
method.executionStatus
)
return (
nameFilter && statusFilter && apkVersionFilter && executionStatusFilter
)
},
testFilter (filter, item) {
item = item || ''
let outerRet =
!filter ||
// Split on '&' operator
filter
.toLowerCase()
.split('&')
.map(x => x.trim())
.map(seg =>
// Split on '|' operator
seg
.split('|')
.map(x => x.trim())
.map(segment => {
let quoted = false,
postOp = x => x
// Check for negation
if (segment.indexOf('!') === 0) {
if (segment.length > 1) {
segment = segment.slice(1, segment.length)
postOp = x => !x
}
}
// Check for quoted
if (segment.indexOf("'") === 0 || segment.indexOf('"') === 0) {
if (segment[segment.length - 1] === segment[0]) {
segment = segment.slice(1, segment.length - 1)
quoted = true
}
}
if (!quoted || segment !== '') {
//console.log(`Item: ${item}, Segment: ${segment}`)
//console.log(`Result: ${item.toLowerCase().includes(segment)}`)
//console.log(`Result': ${postOp(item.toLowerCase().includes(segment))}`)
}
let innerRet =
quoted && segment === ''
? postOp(!item)
: postOp(item.toLowerCase().includes(segment))
//console.log(`InnerRet(${filter}, ${item}): ${innerRet}`)
return innerRet
})
.reduce((x, y) => x || y, false)
)
.reduce((x, y) => x && y, true)
//console.log(`OuterRet(${filter}, ${item}): ${outerRet}`)
return outerRet
},
execute (methID, klassI, methI) {
this.project.classes[klassI].methods[methI].executionStatus = 'QUEUED'
// Make HTTP request to execute method
this.$http.post('/api/Method/' + methID + '/Execute').then(
response => {},
error => console.log("Couldn't execute Test: " + JSON.stringify(error))
)
},
registerExecutorSocket () {
const socket = new WebSocket('ws://localhost:4567/api/Executor/')
socket.onmessage = msg => {
const { methodID, report, executionStatus } = JSON.parse(msg.data)
for (let i = 0; i < this.project.classes.length; ++i) {
const klass = this.project.classes[i]
for (let j = 0; j < klass.methods.length; ++j) {
const meth = klass.methods[j]
if (meth.id === methodID) {
if (report)
this.project.classes[i].methods[j].latestReport = report
if (executionStatus)
this.project.classes[i].methods[j].executionStatus =
executionStatus
return
}
}
}
}
},
prettyName: function (name) {
const split = name.split('.')
return split[split.length - 1]
}
},
components: {
ClassView: ClassView
}
}
</script>
<style scoped></style>
filteredClasses
is being run each time theproject.classes
data is changing but the return valueret
doesn't update? – Ledezmaret
is being modified correctly. Vue just isn't taking that value and updatingvm.computed.filteredClasses
– Writeconsole.log(JSON.stringify(ret))
is showing the correct value butreturn ret
is broken. There has to be something else going on, there's no reason there should be a problem. When you say "filteredClasses
never changes after the initial caching". What exactly do you mean? In the UI? Lastly, are you certain you don't have a method or data property also calledfilteredClasses
? – Ledezma