From 425840e6eb843b1d19fdde915fd68ba4f15f89f9 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sun, 10 May 2026 17:06:05 -0400 Subject: [PATCH 1/3] fix(angular-form): prevent full array re-renders in array mode --- .changeset/fresh-trams-joke.md | 5 ++ docs/framework/angular/guides/arrays.md | 4 +- .../angular/array/src/app/app.component.ts | 2 +- packages/angular-form/src/tanstack-field.ts | 65 +++++++++++++++---- 4 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 .changeset/fresh-trams-joke.md diff --git a/.changeset/fresh-trams-joke.md b/.changeset/fresh-trams-joke.md new file mode 100644 index 000000000..f5e9593df --- /dev/null +++ b/.changeset/fresh-trams-joke.md @@ -0,0 +1,5 @@ +--- +'@tanstack/angular-form': patch +--- + +prevent full array re-renders in array mode diff --git a/docs/framework/angular/guides/arrays.md b/docs/framework/angular/guides/arrays.md index 9b1580257..742c62ba1 100644 --- a/docs/framework/angular/guides/arrays.md +++ b/docs/framework/angular/guides/arrays.md @@ -15,7 +15,7 @@ To use an array, you can use `field.api.state.value` on an array value: standalone: true, imports: [TanStackField], template: ` - +
@for (_ of people.api.state.value; track $index) { @@ -101,7 +101,7 @@ export class AppComponent { template: `
- +
@for (_ of people.api.state.value; track $index) {
- +
@for (_ of people.api.state.value; track $index) { { - return [ - state.meta, - Object.keys((state.value as unknown) ?? []).length, - ] - } - : undefined, - { - injector: this.injector, - }, + isArrayMode + ? (state) => Object.keys((state.value as unknown) ?? []).length + : (state) => state.value, + injectorOpts, + ) + const reactiveIsTouched = injectStore( + this._api().store, + (state) => state.meta.isTouched, + injectorOpts, + ) + const reactiveIsBlurred = injectStore( + this._api().store, + (state) => state.meta.isBlurred, + injectorOpts, + ) + const reactiveIsDirty = injectStore( + this._api().store, + (state) => state.meta.isDirty, + injectorOpts, + ) + const reactiveErrorMap = injectStore( + this._api().store, + (state) => state.meta.errorMap, + injectorOpts, + ) + const reactiveErrorSourceMap = injectStore( + this._api().store, + (state) => state.meta.errorSourceMap, + injectorOpts, + ) + const reactiveIsValidating = injectStore( + this._api().store, + (state) => state.meta.isValidating, + injectorOpts, ) effect( () => { - // Load bearing change detection check - const _values = vals() + // Load bearing change detection check — read every reactive source so + // the effect runs whenever any of them change. + reactiveValue() + reactiveIsTouched() + reactiveIsBlurred() + reactiveIsDirty() + reactiveErrorMap() + reactiveErrorSourceMap() + reactiveIsValidating() this.cd.markForCheck() }, { injector: this.injector }, From 16e18185a984bd9b86aa1691d3d8f563cb15ce58 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 21:07:08 +0000 Subject: [PATCH 2/3] ci: apply automated fixes and generate docs --- examples/angular/array/src/app/app.component.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/angular/array/src/app/app.component.ts b/examples/angular/array/src/app/app.component.ts index a4bf84a55..9aefb8f2f 100644 --- a/examples/angular/array/src/app/app.component.ts +++ b/examples/angular/array/src/app/app.component.ts @@ -8,7 +8,12 @@ import { TanStackField, injectForm, injectStore } from '@tanstack/angular-form' template: `
- +
@for (_ of people.api.state.value; track $index) { Date: Sun, 10 May 2026 17:10:50 -0400 Subject: [PATCH 3/3] chore: fix build --- packages/angular-form/src/tanstack-field.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/angular-form/src/tanstack-field.ts b/packages/angular-form/src/tanstack-field.ts index c1befa2fe..bb5bfdfd9 100644 --- a/packages/angular-form/src/tanstack-field.ts +++ b/packages/angular-form/src/tanstack-field.ts @@ -246,9 +246,10 @@ export class TanStackField< const isArrayMode = this.mode() === 'array' const reactiveValue = injectStore( this._api().store, - isArrayMode - ? (state) => Object.keys((state.value as unknown) ?? []).length - : (state) => state.value, + (state) => + isArrayMode + ? Object.keys((state.value as unknown) ?? []).length + : state.value, injectorOpts, ) const reactiveIsTouched = injectStore(