Skip to content

Commit f80b51a

Browse files
mmalerbacrisbeto
authored andcommitted
refactor(forms): track arrays in a parent array by index
This commit changes arrays in a parent array to be tracked the same way as primitive values like strings and numbers. This is necessary because the tracking key symbol used to maintain identity for objects in an array does not survive the array spread operation: ``` return {...oldValue} // tracking symbol preserved ✅ return [...oldValue] // tracking symbol lost ❌ ```
1 parent 46d5670 commit f80b51a

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

packages/forms/signals/src/field/structure.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import {LogicNode} from '../schema/logic_node';
2020
import type {FieldPathNode} from '../schema/path_node';
2121
import {deepSignal} from '../util/deep_signal';
2222
import {isArray, isObject} from '../util/type_guards';
23+
import type {FieldAdapter} from './field_adapter';
2324
import type {FormFieldManager} from './manager';
2425
import type {FieldNode, ParentFieldNode} from './node';
25-
import type {FieldAdapter} from './field_adapter';
2626

2727
/**
2828
* Key by which a parent `FieldNode` tracks its children.
@@ -406,7 +406,7 @@ function makeChildrenMapSignal(
406406
continue;
407407
}
408408

409-
if (isValueArray && isObject(childValue)) {
409+
if (isValueArray && isObject(childValue) && !isArray(childValue)) {
410410
// For object values in arrays, assign a synthetic identity instead.
411411
trackingId = (childValue[identitySymbol] as TrackingKey) ??= Symbol(
412412
ngDevMode ? `id:${globalId++}` : '',

packages/forms/signals/test/node/field_node.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,30 @@ describe('FieldNode', () => {
614614
expect(f[0] === kirill).toBeTrue();
615615
expect(f[1] === alex).toBeTrue();
616616
});
617+
618+
it('uses index as identity for primitive values', () => {
619+
const value = signal([1, 'two']);
620+
const f = form(value, {injector: TestBed.inject(Injector)});
621+
const first = f[0];
622+
const second = f[1];
623+
624+
value.update((old) => [old[1], old[0]]);
625+
626+
expect(f[0] === first).toBeTrue();
627+
expect(f[1] === second).toBeTrue();
628+
});
629+
630+
it('uses index as identity for array values', () => {
631+
const value = signal([[1], ['two']]);
632+
const f = form(value, {injector: TestBed.inject(Injector)});
633+
const first = f[0];
634+
const second = f[1];
635+
636+
value.update((old) => [old[1], old[0]]);
637+
638+
expect(f[0] === first).toBeTrue();
639+
expect(f[1] === second).toBeTrue();
640+
});
617641
});
618642
});
619643

0 commit comments

Comments
 (0)