N
Nuxt3d ago
Eternal

Update variable on UInput change

How do I make Nuxt UI update a ref whenever the UInput is changed? I tried using v-model and @input but it doesn't work. I'm showing the current value of the ref in a UCard Expected behavior: User types a value into the input The value shown in the card updates Actual behavior: User types into input Card does not update Code:
<script setup>
import * as z from 'zod'
const modifyAnnouncementForm = z.object({
calendarEventId: z.union([z.number(), z.null()]),
});
type ModifyAnnouncementForm = z.output<typeof modifyAnnouncementForm>;

const modifyingAnnouncementFormState =
ref <
ModifyAnnouncementForm >
{
calendarEventId: null,
};
const openModifyModal = ref(false);
</script>

<template>
<UModal
v-model:open="openModifyModal"
:title="`Modifying &quot;${modifyingAnnouncementFormState.title}&quot;`"
:ui="{ footer: 'justify-end' }"
:close="{
color: 'error',
variant: 'subtle',
class: 'rounded-full',
}"
>
<template #body>
<UForm
:schema="modifyAnnouncementForm"
:state="modifyingAnnouncementFormState"
class="space-y-4"
>
<div class="calendarEvent">
<UFormField label="Calendar Event ID" name="calendarEventId">
<UInput
v-model="modifyingAnnouncementFormState.calendarEventId"
type="number"
@input="modifyingAnnouncementFormState.calendarEventId = (($event.target as HTMLInputElement).value as unknown as number)"
/>
</UFormField>

<UCard variant="subtle" class="calendarEventDetails">
<template #header>
<h1>{{ modifyingAnnouncementFormState.calendarEventId }}</h1>
</template>
</UCard>
</div>

<UButton type="submit"> Submit </UButton>
</UForm>
</template>
</UModal>
</template>
<script setup>
import * as z from 'zod'
const modifyAnnouncementForm = z.object({
calendarEventId: z.union([z.number(), z.null()]),
});
type ModifyAnnouncementForm = z.output<typeof modifyAnnouncementForm>;

const modifyingAnnouncementFormState =
ref <
ModifyAnnouncementForm >
{
calendarEventId: null,
};
const openModifyModal = ref(false);
</script>

<template>
<UModal
v-model:open="openModifyModal"
:title="`Modifying &quot;${modifyingAnnouncementFormState.title}&quot;`"
:ui="{ footer: 'justify-end' }"
:close="{
color: 'error',
variant: 'subtle',
class: 'rounded-full',
}"
>
<template #body>
<UForm
:schema="modifyAnnouncementForm"
:state="modifyingAnnouncementFormState"
class="space-y-4"
>
<div class="calendarEvent">
<UFormField label="Calendar Event ID" name="calendarEventId">
<UInput
v-model="modifyingAnnouncementFormState.calendarEventId"
type="number"
@input="modifyingAnnouncementFormState.calendarEventId = (($event.target as HTMLInputElement).value as unknown as number)"
/>
</UFormField>

<UCard variant="subtle" class="calendarEventDetails">
<template #header>
<h1>{{ modifyingAnnouncementFormState.calendarEventId }}</h1>
</template>
</UCard>
</div>

<UButton type="submit"> Submit </UButton>
</UForm>
</template>
</UModal>
</template>
15 Replies
kapa.ai
kapa.ai3d ago
Hi @Eternal. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To update a variable when using Nuxt UI's UInput, you should use the v-model directive. This will automatically keep your ref or reactive variable in sync with the input value. You do not need to use the @input event handler for this purpose—v-model is sufficient and recommended. Key points: - v-model on UInput will update your variable reactively as the user types. - If your variable is a ref or a property of a reactive object, the UI will update automatically wherever you use that variable, such as inside a UCard. Example from the Nuxt UI documentation:
<script setup lang="ts">
const value = ref('')
</script>

<template>
<UInput v-model="value" />
<UCard>
<template #header>
<h1>{{ value }}</h1>
</template>
</UCard>
</template>
<script setup lang="ts">
const value = ref('')
</script>

<template>
<UInput v-model="value" />
<UCard>
<template #header>
<h1>{{ value }}</h1>
</template>
</UCard>
</template>
As the user types in the input, the value in the card updates immediately, thanks to Vue's reactivity and v-model binding on UInput Nuxt UI Input Usage. In your code: You are using v-model="modifyingAnnouncementFormState.calendarEventId" on UInput, which is correct. However, you are also using an @input handler that manually sets the value, which is unnecessary and may cause issues. Remove the @input handler:
<UInput
v-model="modifyingAnnouncementFormState.calendarEventId"
type="number"
/>
<UInput
v-model="modifyingAnnouncementFormState.calendarEventId"
type="number"
/>
Note:
If modifyingAnnouncementFormState is a ref holding an object, you should access its value as modifyingAnnouncementFormState.value.calendarEventId in the script, but in the template, Vue automatically unwraps refs, so your usage is correct. Summary:
- Use only v-model for two-way binding with UInput. - Remove the @input handler. - The card will update automatically as the input changes. For more details, see the official Nuxt UI Input documentation here. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3d ago
<@705244213414985758> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Eternal
EternalOP3d ago
@kapa.ai Why doesn't attaching a watch() work? The value updates now, but watch doesn't do anything
watch(modifyingAnnouncementFormState.calendarEventId, (newValue, oldValue) => {
console.log(newValue)
})
watch(modifyingAnnouncementFormState.calendarEventId, (newValue, oldValue) => {
console.log(newValue)
})
kapa.ai
kapa.ai3d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- The reason your watch() is not working as expected is likely due to how Vue's reactivity system works with refs and objects. If modifyingAnnouncementFormState is a ref holding an object, then modifyingAnnouncementFormState.calendarEventId is not a ref itself—it's just a property on the object. Vue cannot deeply track changes to properties inside a ref object unless you watch the whole object or use a deep watcher. From the knowledge sources: - If you want to watch a property inside a ref object, you should use a getter function in your watcher, like this:
watch(
() => modifyingAnnouncementFormState.value.calendarEventId,
(newValue, oldValue) => {
console.log(newValue)
}
)

watch(
() => modifyingAnnouncementFormState.value.calendarEventId,
(newValue, oldValue) => {
console.log(newValue)
}
)

This ensures Vue tracks the correct dependency and triggers the watcher when calendarEventId changes Vue Ref no longer reactive after being returned from composable. - Watching a property directly (e.g., modifyingAnnouncementFormState.calendarEventId) will not work if that property is not a ref itself, as Vue's reactivity system won't track it in this way Nuxt 3/Vue 3 Reactivity issue, while rendering component in v-for loop. Summary:
Use a getter function in your watcher to track changes to properties inside a ref object. This is a common pattern in Vue 3 and Nuxt 3 for ensuring reactivity with nested properties. If you need to watch a property of a reactive object or a ref holding an object, always use a function as the first argument to watch(). --- References:
- Vue Ref no longer reactive after being returned from composable - Nuxt 3/Vue 3 Reactivity issue, while rendering component in v-for loop ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3d ago
<@705244213414985758> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Eternal
EternalOP3d ago
@kapa.ai It only works if the variable I'm watching is a ref, not a reactive
kapa.ai
kapa.ai3d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You're correct: in Vue 3 (and therefore Nuxt 3), if you want to watch a property inside a ref that holds an object, you need to use a getter function in your watcher, like () => myRef.value.someProp. This works because Vue can track the dependency chain through the getter. However, if you use a reactive object, you can watch its properties directly, but you must also use a getter function. For example:
const state = reactive({ foo: 1 })
watch(() => state.foo, (newVal) => {
console.log(newVal)
})
const state = reactive({ foo: 1 })
watch(() => state.foo, (newVal) => {
console.log(newVal)
})
If you try to watch state.foo directly (without the getter), it won't work, because Vue expects a ref, a reactive object, or a getter function as the first argument to watch (see example). So, whether you're using a ref holding an object or a reactive object, you should always use a getter function in the watcher to ensure reactivity: - For a ref object: watch(() => myRef.value.someProp, ...) - For a reactive object: watch(() => myReactive.someProp, ...) This is a common source of confusion, but it's the expected behavior in Vue 3 and Nuxt 3 (see discussion). If you still find that watching a property of a reactive object doesn't work, double-check that you're using a getter function as the first argument to watch. If you use the property directly, Vue will not track it reactively. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3d ago
<@705244213414985758> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Eternal
EternalOP3d ago
@kapa.ai It doesn't work even if I use a getter for watching a reactive
kapa.ai
kapa.ai3d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'm sorry, but the knowledge sources provided do not contain explicit information about why watching a property of a reactive object with a getter might not work in Nuxt 3 or Vue 3. The sources do confirm that using a getter function is the correct approach for watching properties inside both ref and reactive objects, and that direct property watching does not work for nested properties in a ref object Nuxt 3/Vue 3 Reactivity issue, while rendering component in v-for loop. However, if you are experiencing issues even when using a getter with a reactive object, the knowledge sources do not provide a specific explanation or troubleshooting steps for this scenario. If you can provide a minimal reproducible example, it may help clarify the issue, but based on the current sources, I cannot give a more detailed answer. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3d ago
<@705244213414985758> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Eternal
EternalOP3d ago
@kapa.ai Code for reproduction:
<script setup>
import * as z from 'zod'
const modifyAnnouncementForm = z.object({
calendarEventId: z.union([z.number(), z.null()]),
});
type ModifyAnnouncementForm = z.output<typeof modifyAnnouncementForm>;

const modifyingAnnouncementFormState =
reactive<
ModifyAnnouncementForm >
{
calendarEventId: null,
};
const openModifyModal = ref(false);

watch(() => modifyingAnnouncementFormState.calendarEventId, (newValue) => {
console.log(newValue)
});
</script>

<template>
<UModal
v-model:open="openModifyModal"
:title="`Modifying &quot;${modifyingAnnouncementFormState.title}&quot;`"
:ui="{ footer: 'justify-end' }"
:close="{
color: 'error',
variant: 'subtle',
class: 'rounded-full',
}"
>
<template #body>
<UForm
:schema="modifyAnnouncementForm"
:state="modifyingAnnouncementFormState"
class="space-y-4"
>
<div class="calendarEvent">
<UFormField label="Calendar Event ID" name="calendarEventId">
<UInput
v-model="modifyingAnnouncementFormState.calendarEventId"
type="number"
/>
</UFormField>

<UCard variant="subtle" class="calendarEventDetails">
<template #header>
<h1>{{ modifyingAnnouncementFormState.calendarEventId }}</h1>
</template>
</UCard>
</div>

<UButton type="submit"> Submit </UButton>
</UForm>
</template>
</UModal>
</template>
<script setup>
import * as z from 'zod'
const modifyAnnouncementForm = z.object({
calendarEventId: z.union([z.number(), z.null()]),
});
type ModifyAnnouncementForm = z.output<typeof modifyAnnouncementForm>;

const modifyingAnnouncementFormState =
reactive<
ModifyAnnouncementForm >
{
calendarEventId: null,
};
const openModifyModal = ref(false);

watch(() => modifyingAnnouncementFormState.calendarEventId, (newValue) => {
console.log(newValue)
});
</script>

<template>
<UModal
v-model:open="openModifyModal"
:title="`Modifying &quot;${modifyingAnnouncementFormState.title}&quot;`"
:ui="{ footer: 'justify-end' }"
:close="{
color: 'error',
variant: 'subtle',
class: 'rounded-full',
}"
>
<template #body>
<UForm
:schema="modifyAnnouncementForm"
:state="modifyingAnnouncementFormState"
class="space-y-4"
>
<div class="calendarEvent">
<UFormField label="Calendar Event ID" name="calendarEventId">
<UInput
v-model="modifyingAnnouncementFormState.calendarEventId"
type="number"
/>
</UFormField>

<UCard variant="subtle" class="calendarEventDetails">
<template #header>
<h1>{{ modifyingAnnouncementFormState.calendarEventId }}</h1>
</template>
</UCard>
</div>

<UButton type="submit"> Submit </UButton>
</UForm>
</template>
</UModal>
</template>
kapa.ai
kapa.ai3d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources, your usage of reactive and the watcher appears correct:
const modifyingAnnouncementFormState = reactive<ModifyAnnouncementForm>({
calendarEventId: null,
});

watch(() => modifyingAnnouncementFormState.calendarEventId, (newValue) => {
console.log(newValue)
});
const modifyingAnnouncementFormState = reactive<ModifyAnnouncementForm>({
calendarEventId: null,
});

watch(() => modifyingAnnouncementFormState.calendarEventId, (newValue) => {
console.log(newValue)
});
However, if the watcher is not firing when you update calendarEventId via v-model on UInput, the knowledge sources do not provide a specific explanation for this issue with Nuxt UI's UInput and a reactive object. The sources do confirm that using a getter function in the watcher is the correct approach for both ref and reactive objects, and that Vue's reactivity system should track such changes Nuxt 3/Vue 3 Reactivity issue, while rendering component in v-for loop, Toggle modal when changing text prop dynamically. There is no mention of a known issue with reactive and UInput in the provided sources. If this is not working in your case, the knowledge sources do not contain enough information to explain why. It may be related to how Nuxt UI's UInput interacts with reactive objects, but this is not documented in the sources above. Summary:
- Your watcher syntax is correct. - The knowledge sources do not explain why it would not work with a reactive object and UInput. - There is no explicit workaround or known issue documented in the sources provided. If you need a guaranteed reactive update, using a ref for modifyingAnnouncementFormState and accessing its properties via .value is a common pattern, but this is not explicitly stated in the sources above. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3d ago
<@705244213414985758> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Eternal
EternalOP3d ago
This issue has not been solved It works when using a ref but it doesn't work when using a reactive Please ping me when you respond

Did you find this page helpful?