mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
In some cases the logic order was not preserved properly when using `apply`. In particular this occurs when some logic is registered on a child of the root, followed by an apply to the root, followed by further logic registered on a child. In this case the final registered logic wound up running before the applied logic.
This happened because `FieldPathNode` for a child path was caching its `LogicNodeBuilder` at creation time. This meant that if the parent's `LogicNodeBuilder` changed (e.g., due to an `apply` call), the child would still be using the old one.
This commit fixes the issue by dynamically resolving the `LogicNodeBuilder` for a child path whenever it is accessed.
(cherry picked from commit fa13a167f1)
53 lines
1.7 KiB
TypeScript
53 lines
1.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.dev/license
|
|
*/
|
|
|
|
import {Injector, signal} from '@angular/core';
|
|
import {TestBed} from '@angular/core/testing';
|
|
import {apply, form, required, schema} from '@angular/forms/signals';
|
|
|
|
describe('structure APIs', () => {
|
|
describe('apply', () => {
|
|
it('should maintain order when applying to child of root path', () => {
|
|
const s = schema<{a: string}>((p) => {
|
|
required(p.a, {message: 'before'});
|
|
apply(p.a, (prop) => {
|
|
required(prop, {message: 'apply'});
|
|
});
|
|
required(p.a, {message: 'after'});
|
|
});
|
|
|
|
const data = signal({a: ''});
|
|
const f = form(data, s, {injector: TestBed.inject(Injector)});
|
|
|
|
expect(f.a().errors()).toEqual([
|
|
jasmine.objectContaining({message: 'before'}),
|
|
jasmine.objectContaining({message: 'apply'}),
|
|
jasmine.objectContaining({message: 'after'}),
|
|
]);
|
|
});
|
|
|
|
it('should maintain order when applying to root path', () => {
|
|
const s = schema<{a: {b: string}}>((p) => {
|
|
required(p.a.b, {message: 'before'});
|
|
apply(p, (prop) => {
|
|
required(prop.a.b, {message: 'apply'});
|
|
});
|
|
required(p.a.b, {message: 'after'});
|
|
});
|
|
|
|
const data = signal({a: {b: ''}});
|
|
const f = form(data, s, {injector: TestBed.inject(Injector)});
|
|
|
|
expect(f.a.b().errors()).toEqual([
|
|
jasmine.objectContaining({message: 'before'}),
|
|
jasmine.objectContaining({message: 'apply'}),
|
|
jasmine.objectContaining({message: 'after'}),
|
|
]);
|
|
});
|
|
});
|
|
});
|