webentwicklung-frage-antwort-db.com.de

Vue.js berechnete Eigenschaft wird nicht aktualisiert

Ich verwende eine berechnete Eigenschaft von Vue.js, stoße jedoch auf ein Problem: Die berechnete Methode IST wird zum richtigen Zeitpunkt aufgerufen, aber der Wert wird von zurückgegeben Die berechnete Methode wird ignoriert!

Meine Methode

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
    }
}

Die Werte, die mit dem console.log -Anweisungen sind korrekt, aber wenn ich filteredClasses in der Vorlage verwende, wird nur erster zwischengespeicherter Wert verwendet, und die Vorlage wird nie aktualisiert. Dies wird durch Vue chrome devtools (filteredClasses ändert sich nie nach dem anfänglichen Caching)) bestätigt.

Kann mir jemand sagen, warum das so ist?

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>
13
Jared Loomis

Ich bin bereits auf ein ähnliches Problem gestoßen und habe es mithilfe einer regulären Methode anstelle einer berechneten Eigenschaft gelöst. Verschieben Sie einfach alles in eine Methode und geben Sie Ihren ret zurück. Offizielle Dokumente

11
peaceman

Sie müssen den Listenelementen im v-for einen eindeutigen Schlüsselwert zuweisen. Wie so ..

<ClassView :klass-raw="klass" :key="klass.id"/>

Andernfalls weiß Vue nicht, welche Elemente aktualisiert werden sollen. Erläuterung hier https://vuejs.org/v2/guide/list.html#key

3
steeleb88

Wenn die berechnete Eigenschaft aktualisiert werden soll, wenn project.classes.someSubProperty ändert sich, diese Untereigenschaft muss existieren, wenn die berechnete Eigenschaft definiert wird. Vue kann das Hinzufügen oder Löschen von Eigenschaften nicht erkennen , nur Änderungen an vorhandenen Eigenschaften.

Dies hat mich bei der Verwendung eines Vuex-Speichers mit einem leeren state -Objekt gebissen. Meine nachfolgenden Statusänderungen würden nicht dazu führen, dass berechnete Eigenschaften, die davon abhängen, erneut ausgewertet werden. Das Hinzufügen expliziter Schlüssel mit Nullwerten zum Veux-Status löste dieses Problem.

Ich bin mir nicht sicher, ob in Ihrem Fall explizite Schlüssel möglich sind, aber es kann hilfreich sein, zu erklären, warum die berechnete Eigenschaft veraltet ist.

Vue Reaktivitätsdokumente für weitere Informationen: https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats

Wenn Sie console.log hinzufügen, bevor Sie zurückkehren, wird der berechnete Wert möglicherweise in filteredClasses angezeigt.

DOM wird jedoch aus irgendeinem Grund nicht aktualisiert.

Dann müssen Sie erzwingen, DOM neu zu rendern.

Der beste Weg zum erneuten Rendern ist einfach Schlüssel hinzufügen als berechneter Wert wie unten.

<div
  :key="JSON.stringify(filteredClasses)" 
  v-for="(klass, classIndex) in filteredClasses"
>
  <ClassView
    :key="classIndex"
    :klass-raw="klass"
  />
</div>

Achtung:

Verwenden Sie keine nicht-primitiven Werte wie Objekte und Arrays als Schlüssel. Verwenden Sie stattdessen Zeichenfolgen oder numerische Werte.

Aus diesem Grund habe ich das Array filteredClasses in einen String konvertiert. (Es kann auch andere Array-> String-Konvertierungsmethoden geben.)

Und ich möchte auch sagen: "Es wird empfohlen, wann immer möglich ein Schlüsselattribut mit v-for bereitzustellen.".

0
Antonio