Alors que Site de référence RFC de l'API Vue Composition a de nombreux scénarios d'utilisation avancée avec le module watch
, il n'y a aucun exemple sur comment regarder les accessoires des composants ?
Il n'est pas non plus mentionné dans page principale de Vue Composition API RFC ou vuejs/composition-api dans Github .
J'ai créé un Codesandbox pour élaborer ce problème.
<template>
<div id="app">
<img width="25%" src="./assets/logo.png">
<br>
<p>Prop watch demo with select input using v-model:</p>
<PropWatchDemo :selected="testValue"/>
</div>
</template>
<script>
import { createComponent, onMounted, ref } from "@vue/composition-api";
import PropWatchDemo from "./components/PropWatchDemo.vue";
export default createComponent({
name: "App",
components: {
PropWatchDemo
},
setup: (props, context) => {
const testValue = ref("initial");
onMounted(() => {
setTimeout(() => {
console.log("Changing input prop value after 3s delay");
testValue.value = "changed";
// This value change does not trigger watchers?
}, 3000);
});
return {
testValue
};
}
});
</script>
<template>
<select v-model="selected">
<option value="null">null value</option>
<option value>Empty value</option>
</select>
</template>
<script>
import { createComponent, watch } from "@vue/composition-api";
export default createComponent({
name: "MyInput",
props: {
selected: {
type: [String, Number],
required: true
}
},
setup(props) {
console.log("Setup props:", props);
watch((first, second) => {
console.log("Watch function called with args:", first, second);
// First arg function registerCleanup, second is undefined
});
// watch(props, (first, second) => {
// console.log("Watch props function called with args:", first, second);
// // Logs error:
// // Failed watching path: "[object Object]" Watcher only accepts simple
// // dot-delimited paths. For full control, use a function instead.
// })
watch(props.selected, (first, second) => {
console.log(
"Watch props.selected function called with args:",
first,
second
);
// Both props are undefined so its just a bare callback func to be run
});
return {};
}
});
</script>
[~ # ~] modifier [~ # ~] : Bien que mon exemple de question et de code ait été initialement avec JavaScript, j'utilise en fait TypeScript. La première réponse de Tony Tom, bien qu'elle fonctionne, a conduit à une erreur de type. Ce qui a été résolu par la réponse de Michal Levý. J'ai donc tagué cette question avec TypeScript
par la suite.
EDIT2 : Voici ma version polie mais nue des câblages réactifs pour ce composant de sélection personnalisé, en plus de <b-form-select>
de bootstrap-vue
(sinon implémentation agnostique mais ce composant sous-jacent émet les événements @input et @change tous les deux, selon que la modification a été effectuée par programme ou par interaction de l'utilisateur).
<template>
<b-form-select
v-model="selected"
:options="{}"
@input="handleSelection('input', $event)"
@change="handleSelection('change', $event)"
/>
</template>
<script lang="ts">
import {
createComponent, SetupContext, Ref, ref, watch, computed,
} from '@vue/composition-api';
interface Props {
value?: string | number | boolean;
}
export default createComponent({
name: 'CustomSelect',
props: {
value: {
type: [String, Number, Boolean],
required: false, // Accepts null and undefined as well
},
},
setup(props: Props, context: SetupContext) {
// Create a Ref from prop, as two-way binding is allowed only with sync -modifier,
// with passing prop in parent and explicitly emitting update event on child:
// Ref: https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
// Ref: https://medium.com/@jithilmt/vue-js-2-two-way-data-binding-in-parent-and-child-components-1cd271c501ba
const selected: Ref<Props['value']> = ref(props.value);
const handleSelection = function emitUpdate(type: 'input' | 'change', value: Props['value']) {
// For sync -modifier where 'value' is the prop name
context.emit('update:value', value);
// For @input and/or @change event propagation
// @input emitted by the select component when value changed <programmatically>
// @change AND @input both emitted on <user interaction>
context.emit(type, value);
};
// Watch prop value change and assign to value 'selected' Ref
watch(() => props.value, (newValue: Props['value']) => {
selected.value = newValue;
});
return {
selected,
handleSelection,
};
},
});
</script>
Je voulais juste ajouter plus de détails à la réponse ci-dessus. Comme Michal l'a mentionné, la venue de props
est un objet et est réactive dans son ensemble. Mais, chaque clé de l'objet props n'est pas réactive en elle-même.
Nous devons ajuster la signature watch
pour une valeur dans l'objet reactive
par rapport à une valeur ref
// watching value of a reactive object (watching a getter)
watch(() => props.selected, (selection, prevSelection) => {
/* ... */
})
// directly watching a ref
const selected = ref(props.selected)
watch(selected, (selection, prevSelection) => {
/* ... */
})
Juste quelques informations supplémentaires, même si ce n'est pas le cas mentionné dans la question: si nous voulons surveiller plusieurs propriétés, on peut passer un tableau au lieu d'une seule référence
// Watching Multiple Sources
watch([ref1, ref2, ...], ([refVal1, refVal2, ...],[prevRef1, prevRef2, ...]) => {
/* ... */
})
Vous pouvez utiliser le chemin pour atteindre une propriété donnée:
watch("props.value", (newValue: Props['value']) => {
selected.value = newValue;
});