Skip to content
Open
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
99 changes: 99 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
}
]
},
{
"label": "preact",
"children": [
{
"label": "Quick Start",
"to": "framework/preact/quick-start"
}
]
},
{
"label": "vue",
"children": [
Expand Down Expand Up @@ -169,6 +178,67 @@
}
]
},
{
"label": "preact",
"children": [
{
"label": "Basic Concepts",
"to": "framework/preact/guides/basic-concepts"
},
{
"label": "Form Validation",
"to": "framework/preact/guides/validation"
},
{
"label": "Dynamic Validation",
"to": "framework/preact/guides/dynamic-validation"
},
{
"label": "Async Initial Values",
"to": "framework/preact/guides/async-initial-values"
},
{
"label": "Arrays",
"to": "framework/preact/guides/arrays"
},
{
"label": "Linked Fields",
"to": "framework/preact/guides/linked-fields"
},
{
"label": "Reactivity",
"to": "framework/preact/guides/reactivity"
},
{
"label": "Listeners",
"to": "framework/preact/guides/listeners"
},
{
"label": "Custom Errors",
"to": "framework/preact/guides/custom-errors"
},
{
"label": "Submission Handling",
"to": "framework/preact/guides/submission-handling"
},
{
"label": "UI Libraries",
"to": "framework/preact/guides/ui-libraries"
},
{
"label": "Focus Management",
"to": "framework/preact/guides/focus-management"
},
{
"label": "Form Composition",
"to": "framework/preact/guides/form-composition"
},
{
"label": "Debugging",
"to": "framework/preact/guides/debugging"
}
]
},
{
"label": "vue",
"children": [
Expand Down Expand Up @@ -432,6 +502,35 @@
}
]
},
{
"label": "preact",
"children": [
{
"label": "Preact Reference",
"to": "framework/preact/reference/index"
},
{
"label": "Variables / Field",
"to": "framework/preact/reference/variables/Field"
},
{
"label": "Functions / useField",
"to": "framework/preact/reference/functions/useField"
},
{
"label": "Functions / useForm",
"to": "framework/preact/reference/functions/useForm"
},
{
"label": "Types / FieldComponent",
"to": "framework/preact/reference/type-aliases/FieldComponent"
},
{
"label": "Types / UseField",
"to": "framework/preact/reference/type-aliases/UseField"
}
]
},
{
"label": "vue",
"children": [
Expand Down
14 changes: 7 additions & 7 deletions docs/framework/angular/guides/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ This will generate the mapped JSX every time you run `pushValue` on `field`:
Finally, you can use a subfield like so:

```angular-html
<ng-container
<ng-container
[tanstackField]="form"
[name]="getPeopleName($index)"
#person="field"
Expand All @@ -57,9 +57,7 @@ Finally, you can use a subfield like so:
<div>Name for person {{ $index }}</div>
<input
[value]="person.api.state.value"
(input)="
person.api.handleChange($any($event).target.value)
"
(input)="person.api.handleChange($any($event).target.value)"
/>
</label>
</div>
Expand All @@ -81,7 +79,10 @@ export class AppComponent {
> See, if we did the following:
>
> ```angular-html
> <ng-container [tanstackField]="form" [name]="'people[' + $index + '].name'"></ng-container>
> <ng-container
> [tanstackField]="form"
> [name]="'people[' + $index + '].name'"
> ></ng-container>
> ```
>
> We'd be running into a TypeScript issue where `"one" + "two"` is `string` rather than the required `"onetwo"` type
Expand Down Expand Up @@ -145,8 +146,7 @@ export class AppComponent {
},
})


getPeopleName = (idx: number) => `people[${idx}].name` as const;
getPeopleName = (idx: number) => `people[${idx}].name` as const

canSubmit = injectStore(this.form, (state) => state.canSubmit)
isSubmitting = injectStore(this.form, (state) => state.isSubmitting)
Expand Down
21 changes: 10 additions & 11 deletions docs/framework/angular/guides/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,19 @@ Example:
`,
})
export class AppComponent {
firstNameValidator: FieldValidateFn<any, any, string, any> = ({
value,
}) =>
firstNameValidator: FieldValidateFn<any, any, string, any> = ({ value }) =>
!value
? 'A first name is required'
: value.length < 3
? 'First name must be at least 3 characters'
: undefined

firstNameAsyncValidator: FieldValidateAsyncFn<any, string, any> =
async ({ value }) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return value.includes('error') && 'No "error" allowed in first name'
}
firstNameAsyncValidator: FieldValidateAsyncFn<any, string, any> = async ({
value,
}) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return value.includes('error') && 'No "error" allowed in first name'
}

form = injectForm({
defaultValues: {
Expand Down Expand Up @@ -176,7 +175,7 @@ import { z } from 'zod'
[validators]="{
onChange: z.string().min(3, 'First name must be at least 3 characters'),
onChangeAsyncDebounceMs: 500,
onChangeAsync: firstNameAsyncValidator
onChangeAsync: firstNameAsyncValidator,
}"
#firstName="field"
>
Expand Down Expand Up @@ -343,8 +342,8 @@ export class AppComponent {
yearsOfExperience: 0,
}

getHobbyName = (idx: number) => `hobbies[${idx}].name` as const;
getHobbyDesc = (idx: number) => `hobbies[${idx}].description` as const;
getHobbyName = (idx: number) => `hobbies[${idx}].name` as const
getHobbyDesc = (idx: number) => `hobbies[${idx}].description` as const

form = injectForm({
defaultValues: {
Expand Down
60 changes: 38 additions & 22 deletions docs/framework/angular/guides/dynamic-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ We support this through our `onDynamic` validation function.

```angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import {
TanStackField,
injectForm,
revalidateLogic,
} from '@tanstack/angular-form'
Comment on lines +13 to +17
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

These Angular documentation formatting changes are unrelated to the PR objective.

This PR is about adding Preact integration, but these changes modify Angular documentation formatting. Consider moving unrelated documentation formatting updates to a separate PR to maintain clear change scope and simplify review.

Also applies to: 90-95, 127-132, 178-182, 244-248, 283-287

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/angular/guides/dynamic-validation.md` around lines 13 - 17,
These Angular documentation formatting edits (changes to imports like
TanStackField, injectForm, revalidateLogic in the dynamic validation docs) are
unrelated to the Preact integration PR—remove or revert these formatting-only
changes from this branch and instead place them in a separate doc/formatting PR;
specifically undo the modified import formatting and any other changes around
the same import blocks (also at the other noted regions) so the current PR only
contains Preact integration code.


@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<!-- Your form template here -->
`,
template: ` <!-- Your form template here --> `,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Documentation regression: Template examples replaced with non-functional placeholders.

Replacing actual template code with placeholder comments like <!-- Your form template here --> significantly reduces the educational value of this guide. Users rely on complete, working examples to understand how to implement dynamic validation.

Additionally, this is inconsistent—some examples (e.g., lines 101, 138-143, 189-213) retain actual template code while these are replaced with placeholders.

Restore the original template examples to maintain documentation quality.

Also applies to: 70-70, 254-254, 294-294

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/angular/guides/dynamic-validation.md` at line 23, The
placeholder template strings (e.g., the property template: ` <!-- Your form
template here --> `) removed the real Angular form examples; restore the
original working template HTML wherever those placeholders appear (including
occurrences at the three other spots referenced) by replacing the placeholder
string with the original form markup used elsewhere in the doc (copy the
concrete template examples from the retained examples around the other sections
such as the form controls and validation message snippets) so each example
includes full, runnable template code; update the same template property
name/variable where it was replaced to ensure consistency across examples.

})
export class AppComponent {
form = injectForm({
Expand Down Expand Up @@ -65,9 +67,7 @@ You can, for example, use the following to revalidate on blur after the first su
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<!-- Your form template here -->
`,
template: ` <!-- Your form template here --> `,
})
export class AppComponent {
form = injectForm({
Expand All @@ -87,15 +87,18 @@ Just as you might access errors from an `onChange` or `onBlur` validation, you c

```angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'
import {
TanStackField,
injectForm,
injectStore,
revalidateLogic,
} from '@tanstack/angular-form'

@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<p>{{ formErrorMap().onDynamic?.firstName }}</p>
`,
template: ` <p>{{ formErrorMap().onDynamic?.firstName }}</p> `,
})
export class AppComponent {
form = injectForm({
Expand All @@ -121,7 +124,12 @@ You can use `onDynamic` validation alongside other validation logic, such as `on

```angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, injectStore, revalidateLogic } from '@tanstack/angular-form'
import {
TanStackField,
injectForm,
injectStore,
revalidateLogic,
} from '@tanstack/angular-form'

@Component({
selector: 'app-root',
Expand Down Expand Up @@ -167,7 +175,11 @@ You can also use `onDynamic` validation with fields, just like you would with ot

```angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import {
TanStackField,
injectForm,
revalidateLogic,
} from '@tanstack/angular-form'
import type { FieldValidateFn } from '@tanstack/angular-form'

@Component({
Expand All @@ -180,7 +192,7 @@ import type { FieldValidateFn } from '@tanstack/angular-form'
[tanstackField]="form"
name="age"
[validators]="{
onDynamic: ageValidator
onDynamic: ageValidator,
}"
#age="field"
>
Expand Down Expand Up @@ -229,15 +241,17 @@ Async validation can also be used with `onDynamic` just like with other validati

```angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import {
TanStackField,
injectForm,
revalidateLogic,
} from '@tanstack/angular-form'

@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<!-- Your form template here -->
`,
template: ` <!-- Your form template here --> `,
})
export class AppComponent {
form = injectForm({
Expand Down Expand Up @@ -266,16 +280,18 @@ You can also use standard schema validation libraries like Valibot or Zod with `

```angular-ts
import { Component } from '@angular/core'
import { TanStackField, injectForm, revalidateLogic } from '@tanstack/angular-form'
import {
TanStackField,
injectForm,
revalidateLogic,
} from '@tanstack/angular-form'
import { z } from 'zod'

@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<!-- Your form template here -->
`,
template: ` <!-- Your form template here --> `,
})
export class AppComponent {
schema = z.object({
Expand Down
14 changes: 3 additions & 11 deletions docs/framework/angular/guides/form-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ import { TanStackField, injectForm, injectStore } from '@tanstack/angular-form'
imports: [TanStackField],
template: `
<div>
<ng-container
[tanstackField]="form"
name="firstName"
#firstName="field"
>
<ng-container [tanstackField]="form" name="firstName" #firstName="field">
<label [for]="firstName.api.name">First Name:</label>
<input
[id]="firstName.api.name"
Expand All @@ -47,11 +43,7 @@ import { TanStackField, injectForm, injectStore } from '@tanstack/angular-form'
</ng-container>
</div>
<div>
<ng-container
[tanstackField]="form"
name="lastName"
#lastName="field"
>
<ng-container [tanstackField]="form" name="lastName" #lastName="field">
<label [for]="lastName.api.name">Last Name:</label>
<input
[id]="lastName.api.name"
Expand Down Expand Up @@ -91,7 +83,7 @@ export class AppComponent {
This is functionally correct, but introduces a _lot_ of repeated templating behavior over and over. Instead, let's move the error handling, label to input binding, and other repeated logic into a component:

```angular-ts
import {injectField} from '@tanstack/angular-form'
import { injectField } from '@tanstack/angular-form'

@Component({
selector: 'app-text-field',
Expand Down
3 changes: 1 addition & 2 deletions docs/framework/angular/guides/listeners.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Events that can be "listened" to are:
[tanstackField]="form"
name="country"
[listeners]="{
onChange: onCountryChange
onChange: onCountryChange,
}"
#country="field"
></ng-container>
Expand All @@ -43,7 +43,6 @@ Events that can be "listened" to are:
></ng-container>
`,
})

export class AppComponent {
form = injectForm({
defaultValues: {
Expand Down
Loading
Loading