@@ -4817,7 +4952,7 @@ class FormGroupComp {
`
})
-class NestedFormGroupComp {
+class NestedFormGroupNameComp {
// TODO(issue/24571): remove '!'.
form!: FormGroup;
}
@@ -4840,6 +4975,20 @@ class FormArrayComp {
cityArray!: FormArray;
}
+@Component({
+ selector: 'nested-form-array-name-comp',
+ template: `
+
+ `
+})
+class NestedFormArrayNameComp {
+ form!: FormGroup;
+}
+
@Component({
selector: 'form-array-nested-group',
template: `
diff --git a/packages/forms/test/template_integration_spec.ts b/packages/forms/test/template_integration_spec.ts
index 8270270ceb..f31adfbb23 100644
--- a/packages/forms/test/template_integration_spec.ts
+++ b/packages/forms/test/template_integration_spec.ts
@@ -187,6 +187,21 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
dispatchEvent(input, 'input');
fixture.detectChanges();
expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
+
+ const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
+ dispatchEvent(formEl, 'submit');
+ fixture.detectChanges();
+
+ expect(sortedClassList(formEl)).toEqual([
+ 'ng-dirty', 'ng-submitted', 'ng-touched', 'ng-valid'
+ ]);
+ expect(sortedClassList(input)).not.toContain('ng-submitted');
+
+ dispatchEvent(formEl, 'reset');
+ fixture.detectChanges();
+
+ expect(sortedClassList(formEl)).toEqual(['ng-pristine', 'ng-untouched', 'ng-valid']);
+ expect(sortedClassList(input)).not.toContain('ng-submitted');
});
}));
@@ -244,9 +259,52 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
expect(sortedClassList(modelGroup)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
expect(sortedClassList(form)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']);
+
+ const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
+ dispatchEvent(formEl, 'submit');
+ fixture.detectChanges();
+
+ expect(sortedClassList(formEl)).toEqual([
+ 'ng-dirty', 'ng-submitted', 'ng-touched', 'ng-valid'
+ ]);
});
}));
+ it('should set status classes involving nested FormGroups', () => {
+ const fixture = initTest(NgModelNestedForm);
+ fixture.componentInstance.first = '';
+ fixture.componentInstance.other = '';
+ fixture.detectChanges();
+
+ const form = fixture.debugElement.query(By.css('form')).nativeElement;
+ const modelGroup = fixture.debugElement.query(By.css('[ngModelGroup]')).nativeElement;
+ const input = fixture.debugElement.query(By.css('input')).nativeElement;
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(sortedClassList(modelGroup)).toEqual(['ng-pristine', 'ng-untouched', 'ng-valid']);
+
+ expect(sortedClassList(form)).toEqual(['ng-pristine', 'ng-untouched', 'ng-valid']);
+
+ const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
+ dispatchEvent(formEl, 'submit');
+ fixture.detectChanges();
+
+ expect(sortedClassList(modelGroup)).toEqual(['ng-pristine', 'ng-untouched', 'ng-valid']);
+ expect(sortedClassList(form)).toEqual([
+ 'ng-pristine', 'ng-submitted', 'ng-untouched', 'ng-valid'
+ ]);
+ expect(sortedClassList(input)).not.toContain('ng-submitted');
+
+ dispatchEvent(formEl, 'reset');
+ fixture.detectChanges();
+
+ expect(sortedClassList(modelGroup)).toEqual(['ng-pristine', 'ng-untouched', 'ng-valid']);
+ expect(sortedClassList(form)).toEqual(['ng-pristine', 'ng-untouched', 'ng-valid']);
+ expect(sortedClassList(input)).not.toContain('ng-submitted');
+ });
+ });
+
it('should not create a template-driven form when ngNoForm is used', () => {
const fixture = initTest(NgNoFormComp);
fixture.detectChanges();
@@ -2367,6 +2425,24 @@ class NgModelNgIfForm {
email!: string;
}
+@Component({
+ selector: 'ng-model-nested',
+ template: `
+
+ `
+})
+class NgModelNestedForm {
+ first!: string;
+ other!: string;
+}
+
@Component({
selector: 'ng-no-form',
template: `