Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,47 @@ import { z } from "zod"
const schema = z.object({ planet: z.string() })

// Create your form with a unique key
const { getFieldState, register, key } = useForm({
const { getFieldState, register, key, setValue } = useForm({
schema,
key: "planet-form-key",
})

// Get the state of the 'planet' field
const planetState = getFieldState("planet")
const hideInput = ref(false)

// random operation to demonstrate updating the planet field programmatically
function updateVal(testUpdate: boolean) {
setValue("planet", (v) => {
const [base, count] = v.split(".")
const recoveredNum = Number(count ?? 0)
const safeNum = Number.isNaN(recoveredNum) ? 0 : recoveredNum
const BASE_DEFAULT = "Jupiter"
return testUpdate ? `${base || BASE_DEFAULT}.${safeNum + 1}` : v
})
}
</script>

<template>
<div>
<h1>Fancy Form "{{ key }}"</h1>

<input
v-if="!hideInput"
v-register="register('planet')"
placeholder="Enter your favorite planet"
>

<p>Favorite Planet field state:</p>
<pre>{{ JSON.stringify(planetState, null, 2) }}</pre>

<button @click="hideInput = !hideInput">
{{ hideInput ? 'show input' : 'hide input' }}
</button>

<button @click="updateVal(true)">
Update value programmatically
</button>
<hr>
</div>
</template>
9 changes: 5 additions & 4 deletions src/runtime/lib/core/composables/use-meta-tracker-store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// import { useState } from "#app"
// import { useState } from "nuxt/app"
import { merge } from "lodash-es"
import { isEqual, merge } from "lodash-es"
import { useState } from "nuxt/app"
import { computed } from "vue"
import type {
Expand Down Expand Up @@ -41,8 +39,11 @@ export function updateMetaTracker(config: UpdateMetaTrackerConfig) {
= typeof basePath === "string"
? metaTracker[basePath]?.updatedAt ?? null
: null

const hasRawValueChanged = basePath === null ? true : !isEqual(metaTracker[basePath]?.rawValue, rawValue)
const newTime = hasRawValueChanged ? new Date().toISOString() : lastKnownTime
const updatedAt
= updateTime ?? true ? new Date().toISOString() : lastKnownTime
= (updateTime ?? true) ? newTime : lastKnownTime

const flattenedObject = flattenObjectWithBaseKey(
rawValue,
Expand Down
2 changes: 0 additions & 2 deletions src/runtime/lib/core/utils/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export function registerFactory<Form extends GenericForm>(
basePath: path,
metaTracker: metaTracker.value,
rawValue: metaTracker.value?.[path]?.rawValue ?? get(form, path),
updateTime: false,
isConnected: true,
})
}
Expand All @@ -72,7 +71,6 @@ export function registerFactory<Form extends GenericForm>(
basePath: path,
metaTracker: metaTracker.value,
rawValue: metaTracker.value?.[path]?.rawValue ?? get(form, path),
updateTime: false, // for consistency, only recompute `updatedAt` when form value changes
isConnected: !!remainingElementCount, // only mark as disconnected if all elements are unmounted
})
},
Expand Down
28 changes: 24 additions & 4 deletions src/runtime/types/types-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,36 @@ export type CustomRegisterDirective<T, Modifiers extends string = string> = Obje
[S: symbol]: CustomDirectiveRegisterAssignerFn
}, RegisterValue, Modifiers, string>

// bring in this RegisterModelDynamicCustomDirective type once PR #12605 in vuejs/core enters production (currently in main but not released)
// https://github.com/vuejs/core/pull/12605
// export type RegisterTextCustomDirective = CustomRegisterDirective<
// HTMLInputElement | HTMLTextAreaElement,
// "trim" | "number" | "lazy"
// >

export type RegisterTextCustomDirective = CustomRegisterDirective<
HTMLInputElement | HTMLTextAreaElement,
"trim" | "number" | "lazy"
HTMLInputElement | HTMLTextAreaElement, string
>

export type RegisterCheckboxCustomDirective = CustomRegisterDirective<HTMLInputElement>
export type RegisterRadioCustomDirective = CustomRegisterDirective<HTMLInputElement>
export type RegisterSelectCustomDirective = CustomRegisterDirective<HTMLSelectElement, "number">

// bring in this RegisterModelDynamicCustomDirective type once PR #12605 in vuejs/core enters production (currently in main but not released)
// https://github.com/vuejs/core/pull/12605
// export type RegisterTextCustomDirective = CustomRegisterDirective<
// HTMLInputElement | HTMLTextAreaElement,
// "trim" | "number" | "lazy"
// >
// export type RegisterSelectCustomDirective = CustomRegisterDirective<HTMLSelectElement, "number">
export type RegisterSelectCustomDirective = CustomRegisterDirective<HTMLSelectElement, string>

// bring in this RegisterModelDynamicCustomDirective type once PR #12605 in vuejs/core enters production (currently in main but not released)
// https://github.com/vuejs/core/pull/12605
// export type RegisterModelDynamicCustomDirective = ObjectDirective<
// HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, RegisterValue, "trim" | "number" | "lazy"
// >
export type RegisterModelDynamicCustomDirective = ObjectDirective<
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, RegisterValue, "trim" | "number" | "lazy"
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, RegisterValue, string
>
export type RegisterDirective =
| RegisterTextCustomDirective
Expand Down