66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { WritableSignal } from '@angular/core' ;
9+ import { computed , Signal , WritableSignal } from '@angular/core' ;
1010import { Field , FieldContext , FieldPath , FieldState } from '../api/types' ;
1111import { DYNAMIC } from '../logic_node' ;
1212import { FieldPathNode , FieldRootPathNode } from '../path_node' ;
@@ -18,50 +18,64 @@ import {FieldNode} from './node';
1818export class FieldNodeContext implements FieldContext < unknown > {
1919 constructor ( private readonly node : FieldNode ) { }
2020
21- private readonly cache = new WeakMap < FieldPath < unknown > , Field < unknown > > ( ) ;
21+ private readonly cache = new WeakMap < FieldPath < unknown > , Signal < Field < unknown > > > ( ) ;
2222 private resolve < U > ( target : FieldPath < U > ) : Field < U > {
23- if ( this . cache . has ( target ) ) {
24- return this . cache . get ( target ) as Field < U > ;
25- }
26- const currentPathKeys = this . node . structure . pathKeys ( ) ;
27- const targetPathNode = FieldPathNode . unwrapFieldPath ( target ) ;
23+ if ( ! this . cache . has ( target ) ) {
24+ const resolver = computed < Field < unknown > > ( ( ) => {
25+ const currentPathKeys = this . node . structure . pathKeys ( ) ;
26+ const targetPathNode = FieldPathNode . unwrapFieldPath ( target ) ;
2827
29- if ( ! ( this . node . structure . root . structure . logicPath instanceof FieldRootPathNode ) ) {
30- throw Error ( 'Expected root of FieldNode tree to have a FieldRootPathNode.' ) ;
31- }
32- const prefix = this . node . structure . root . structure . logicPath . subroots . get ( targetPathNode . root ) ;
33- if ( ! prefix ) {
34- throw Error ( 'Path is not part of this field tree.' ) ;
35- }
28+ if ( ! ( this . node . structure . root . structure . logicPath instanceof FieldRootPathNode ) ) {
29+ throw Error ( 'Expected root of FieldNode tree to have a FieldRootPathNode.' ) ;
30+ }
31+ const prefix = this . node . structure . root . structure . logicPath . subroots . get (
32+ targetPathNode . root ,
33+ ) ;
34+ if ( ! prefix ) {
35+ throw Error ( 'Path is not part of this field tree.' ) ;
36+ }
3637
37- const targetPathKeys = [ ...prefix , ...targetPathNode . keys ] ;
38+ const targetPathKeys = [ ...prefix , ...targetPathNode . keys ] ;
3839
39- // Navigate from `currentPath` to `targetPath`. As an example, suppose that:
40- // currentPath = [A, B, C, D]
41- // targetPath = [A, B, X, Y, Z]
40+ // Navigate from `currentPath` to `targetPath`. As an example, suppose that:
41+ // currentPath = [A, B, C, D]
42+ // targetPath = [A, B, X, Y, Z]
4243
43- // Firstly, find the length of the shared prefix between the two paths. In our example, this
44- // is the prefix [A, B], so we would expect a `sharedPrefixLength` of 2.
45- const sharedPrefixLength = lengthOfSharedPrefix ( currentPathKeys , targetPathNode . keys ) ;
44+ // Firstly, find the length of the shared prefix between the two paths. In our example, this
45+ // is the prefix [A, B], so we would expect a `sharedPrefixLength` of 2.
46+ const sharedPrefixLength = lengthOfSharedPrefix ( currentPathKeys , targetPathNode . keys ) ;
4647
47- // Walk up the graph until we arrive at the common ancestor, which could be the root node if
48- // there is no shared prefix. In our example, this will require 2 up steps, navigating from
49- // D to B.
50- let requiredUpSteps = currentPathKeys . length - sharedPrefixLength ;
51- let field : FieldNode = this . node ;
52- while ( requiredUpSteps -- > 0 ) {
53- field = field . structure . parent ! ;
54- }
48+ // Walk up the graph until we arrive at the common ancestor, which could be the root node if
49+ // there is no shared prefix. In our example, this will require 2 up steps, navigating from
50+ // D to B.
51+ let requiredUpSteps = currentPathKeys . length - sharedPrefixLength ;
52+ let field : FieldNode | undefined = this . node ;
53+ while ( requiredUpSteps -- > 0 ) {
54+ field = field . structure . parent ! ;
55+ }
5556
56- // Now, we can navigate from the closest ancestor to the target, e.g. from B through X, Y,
57- // and then to Z.
58- for ( let idx = sharedPrefixLength ; idx < targetPathKeys . length ; idx ++ ) {
59- const property = targetPathKeys [ idx ] === DYNAMIC ? currentPathKeys [ idx ] : targetPathKeys [ idx ] ;
60- field = field . structure . getChild ( property ) ! ;
61- }
57+ // Now, we can navigate from the closest ancestor to the target, e.g. from B through X, Y,
58+ // and then to Z.
59+ for (
60+ let idx = sharedPrefixLength ;
61+ field !== undefined && idx < targetPathKeys . length ;
62+ idx ++
63+ ) {
64+ const property =
65+ targetPathKeys [ idx ] === DYNAMIC ? currentPathKeys [ idx ] : targetPathKeys [ idx ] ;
66+ field = field . structure . getChild ( property ) ;
67+ }
68+
69+ if ( field === undefined ) {
70+ throw new Error ( `Resolved field does not exist` ) ;
71+ }
6272
63- this . cache . set ( target , field . fieldProxy ) ;
64- return field . fieldProxy as Field < U > ;
73+ return field . fieldProxy ;
74+ } ) ;
75+
76+ this . cache . set ( target , resolver ) ;
77+ }
78+ return this . cache . get ( target ) ! ( ) as Field < U > ;
6579 }
6680
6781 get field ( ) : Field < unknown > {
0 commit comments