简体   繁体   English

TypeScript VueJS:使用具有计算属性的观察器

[英]TypeScript VueJS: Using watcher with computed property

I'm building a custom component that will allow me to use arbitrary <div> s as radio buttons.我正在构建一个自定义组件,它允许我使用任意<div>作为单选按钮。 Currently my code looks like so:目前我的代码如下所示:

<template>
    <div
        class="radio-div"
        :class="selected ? 'selected' : ''"
        @click="handleClick"
    >
        <slot></slot>
    </div>
</template>

<style lang="scss" scoped>
.radio-div {
    display: inline-block;
    border-radius: 5px;
    border: 1px solid #000;
    padding: 5px;
    margin: 5px;
}

.radio-div.selected {
    box-shadow: 0 0 10px rgba(255, 255, 0, 0.5);
    border: 2px solid #000;
}
</style>

<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";

@Component
export default class RadioDiv extends Vue {
    @Prop()
    val!: string;

    @Prop({
        required: true
    })
    value!: string;

    selected = false;

    mounted() {
        this.selected = this.value === this.val;
    }

    @Watch("value")
    onChange() {
        this.selected = this.value === this.val;
    }

    handleClick(e: MouseEvent) {
        this.$emit("input", this.val);
    }
}
</script>

To utilize this I can put it in a template like so:为了利用它,我可以把它放在一个模板中,如下所示:

<template>
    <div class="q-pa-md">
        <q-card>
            <q-card-section class="bg-secondary">
                <div class="text-h6">Political Affiliation</div>
            </q-card-section>
            <q-separator />
            <q-card-section>
                <radio-div v-model="politicalParty" val="Republican">
                    <div class="text-h6">Republican</div>
                    <p>Wrong answer</p>
                </radio-div>
                <radio-div v-model="politicalParty" val="Democrat">
                    <div class="text-h6">Democrat</div>
                    <p>Wrong answer</p>
                </radio-div>
                <radio-div v-model="politicalParty" val="Independent">
                    <div class="text-h6">Independent</div>
                    <p>These people clearly know what's up</p>
                </radio-div>
            </q-card-section>
        </q-card>
    </div>
</template>

<script lang="ts">
import { Vue, Component, Watch } from "vue-property-decorator";
import RadioDiv from "../components/RadioDiv.vue";

@Component({
    components: { RadioDiv }
})
export default class Profile extends Vue {
    politicalParty = "Independent";
}
</script>

This works as expected.这按预期工作。 I can click on the <div> s and it switches which one is selected and updates the variable appropriately.我可以单击<div>并切换选择哪一个并适当地更新变量。

But now I want to tie this into a global state manager.但现在我想将它与全局状态管理器联系起来。 So instead of a local politicalParty variable, I have a computed property like so:因此,我有一个计算属性,而不是本地politicalParty党变量,如下所示:

<script lang="ts">
import { Vue, Component, Watch } from "vue-property-decorator";
import RadioDiv from "../components/RadioDiv.vue";
import globalState from "../globalState";

@Component({
    components: { RadioDiv }
})
export default class Profile extends Vue {
    get politicalParty() {
        return globalState.politicalParty;
    }

    set politicalParty(val) {
        globalState.politicalParty = val;
    }
}
</script>

Putting a console.log statement in the setter I can see that it is getting called, and the variable is being updated.在 setter 中放置一个console.log语句,我可以看到它正在被调用,并且变量正在被更新。 But putting a console.log statement in my value watcher (in the RadioDiv component) shows it's no longer being called now that I'm using computed properties.但是在我的value观察器(在RadioDiv组件中)中放置一个console.log语句表明它现在不再被调用,因为我正在使用计算属性。

What's the secret to get my RadioDiv reactive again, now that I'm using global state?既然我正在使用全局状态,那么让我的RadioDiv再次响应的秘诀是什么?

Update更新

The issue doesn't seem to be specific to my custom components, or to watchers.这个问题似乎并不特定于我的自定义组件或观察者。 I decided to ignore this and move on while waiting for an answer from StackOverflow and ran into the issue again with Quasar's components:我决定忽略这一点,继续等待 StackOverflow 的回答,然后再次使用 Quasar 的组件遇到问题:

<template>
...
            <q-card-section>
                <div class="row">
                    <div class="col">
                        <q-slider v-model="age" :min="0" :max="100" />
                    </div>
                    <div class="col">
                        {{ ageText }}
                    </div>
                </div>
            </q-card-section>
...
</template>

<script lang="ts">
...
    get age() {
        return globalState.age;
    }

    set age(val) {
        globalState.age = val;
        this.ageText = "You are " + val + " years old";
    }
...
</script>

This led me to try using no custom components whatsoever:这导致我尝试不使用任何自定义组件:

<template>
...
    <input type="text" v-model="test" />
    <p>Text: {{ test }}</p>
...
</template>

<script lang="ts">
let testVal = "";
...
    get test() { return testVal; }
    set test(val) { testVal = val; }
...
</script>

Once again: No reactivity.再一次:没有反应。 When I use a computed property with v-model nothing seems to change after the call to set当我将计算属性与v-model一起使用时,调用set后似乎没有任何变化

If globalState were just an Object , then it would not be reactive, so computed is only going to read its value once .如果globalState只是一个Object ,那么它不会是响应式的,因此computed只会读取一次它的值。 Same for testVal , which is just a String (also not reactive itself). testVal也一样,它只是一个String (本身也不是反应式的)。

To make the test computed prop reactive to testVal , create testVal with Vue.observable() :要使test计算的 prop 对testVal具有反应性,请使用Vue.observable()创建testVal

const testVal = Vue.observable({ x: '' })

@Component
export default class Profile extends Vue {
  get test() { return testVal.x }
  set test(val) { testVal.x = val }
}

Similarly for globalState , exporting a Vue.observable() would allow your computed props to be reactive:globalState类似,导出Vue.observable()将使您的计算道具具有反应性:

// globalState.js
export default Vue.observable({
  politicalParty: ''
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM