Obtengo datos de la API con los que estoy completando el formulario en mi componente. Necesito activar a los observadores solo después del llenado inicial de datos. Como de forma asíncrona. Pero el observador se activa de inmediato. Necesito deshabilitar el botón Actualizar solo si se cambia algún valor después del llenado inicial de datos.
<template> <div id="app"> <input type="text" v-model="user.userId" /> <br /> <br /> <input type="text" v-model="user.title" /> <br /> <br /> <button :disabled="isDisabled">Update</button> </div> </template> <script> export default { name: "App", watch: { user: { handler(oldVal, newVal) { if (oldVal != newVal) { this.isLoaded = false; } }, deep: true, }, }, computed: { isDisabled() { return this.isLoaded; }, }, async created() { await fetch("https://jsonplaceholder.typicode.com/todos/1") .then((response) => response.json()) .then((json) => { this.user = json; this.isLoaded = true; }); }, data() { return { user: { userId: 0, id: 0, title: "", completed: false, }, isLoaded: true, }; }, }; </script>Me he referido a Vue, espero a Watch y ¿Son los relojes asíncronos? y Vue.js Cómo observar antes de montar() , no puedo obtener datos del reloj pero no puedo seguir.
Aquí hay una vista previa: https://codesandbox.io/embed/great-euler-skd3v?fontsize=14&hidenavigation=1&theme=dark
Esto debe determinarse con alguna condición.
isLoaded ya sirve para determinar el estado de carga inicial, pero el nombre es confuso porque determina que los datos no se cargan.
Puede ser:
watch: { user: { if (this.isLoading && oldVal != newVal) { this.isLoading = false; } ... El observador no necesita ser deep y se puede dejar de ver cuando no se necesita:
async created() { let unwatchUser = this.$watch('user', (oldVal, newVal) => { if (this.isLoading && oldVal != newVal) { this.isLoading = false; unwatchUser(); } }) ... Una forma común de indicar que los datos aún no se han cargado es establecerlos en null , es decir, sin valor. Esto no necesita el indicador isLoading o un observador. Si null no es deseable debido a las propiedades del objeto al que se hace referencia, esto se puede solucionar con el encadenamiento opcional y la representación condicional:
<div v-if="user"> <input type="text" v-model="user.userId" /> ... <div v-else class="spinner"/>La respuesta más simple para la pregunta:
P: ¿Cómo ver solo después de la carga inicial de la API en VueJS?
R: Agregue una bandera dentro de su
watch(por ejemploisLoaded).
También hay un par de cosas mal con su código:
async/await en created no hace nada,isDisabled no es necesario porque se basa solo en 1 valor de data . En su lugar, puede usar este valor ( isLoading ).isLoading no cambiará, un mejor enfoque es moverlo a finalmente .Solución de su problema ( codesandbox ):
<template> <div id="app"> <div v-if="!isFetching"> <input type="text" v-model="user.userId" /> <br /> <br /> <input type="text" v-model="user.title" /> <br /> <br /> <button :disabled="!isLoaded">Update</button> </div> <div v-else>Loading...</div> </div> </template> <script> export default { name: "App", data() { return { user: { userId: 0, id: 0, title: "", completed: false, }, isFetching: false, isLoaded: false }; }, watch: { user: { handler(oldVal, newVal) { if (!this.isFetching) { // this comparision doesn't work (cause oldVal/newVal is an object) if (oldVal != newVal) { this.isLoaded = false; } } }, deep: true }, }, created() { this.isFetching = true; fetch("https://jsonplaceholder.typicode.com/todos/1") .then((response) => response.json()) .then((json) => { this.user = json; this.isLoaded = true; }) .finally(() => this.isFetching = false) }, }; </script>