Revert "feat(forms): add ng-submitted class to forms that have been submitted." (#42474)
				
					
				
			This reverts commit f024d7556081f8913f21761bb8e6aab8d08be110. PR Close #42474
This commit is contained in:
		
							parent
							
								
									ae858c0504
								
							
						
					
					
						commit
						00b1444d12
					
				| @ -195,7 +195,6 @@ The following classes are currently supported. | ||||
| * `.ng-dirty` | ||||
| * `.ng-untouched` | ||||
| * `.ng-touched` | ||||
| * `.ng-submitted` (enclosing form element only) | ||||
| 
 | ||||
| In the following example, the hero form uses the `.ng-valid` and `.ng-invalid` classes to | ||||
| set the color of each form control's border. | ||||
|  | ||||
| @ -314,8 +314,6 @@ Angular sets special CSS classes on the control element to reflect the state, as | ||||
| 
 | ||||
| </table> | ||||
| 
 | ||||
| Additionally, Angular applies the `ng-submitted` class to `<form>` elements upon submission. This class does *not* apply to inner controls. | ||||
| 
 | ||||
| You use these CSS classes to define the styles for your control based on its status. | ||||
| 
 | ||||
| ### Observe control states | ||||
|  | ||||
							
								
								
									
										2
									
								
								goldens/public-api/forms/forms.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								goldens/public-api/forms/forms.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -460,7 +460,7 @@ export declare class RadioControlValueAccessor extends ɵangular_packages_forms_ | ||||
|     name: string; | ||||
|     onChange: () => void; | ||||
|     value: any; | ||||
|     constructor(renderer: Renderer2, elementRef: ElementRef, _registry: ɵangular_packages_forms_forms_r, _injector: Injector); | ||||
|     constructor(renderer: Renderer2, elementRef: ElementRef, _registry: ɵangular_packages_forms_forms_q, _injector: Injector); | ||||
|     fireUncheck(value: any): void; | ||||
|     ngOnDestroy(): void; | ||||
|     ngOnInit(): void; | ||||
|  | ||||
| @ -12,8 +12,7 @@ import {AbstractControlDirective} from './abstract_control_directive'; | ||||
| import {ControlContainer} from './control_container'; | ||||
| import {NgControl} from './ng_control'; | ||||
| 
 | ||||
| type AnyControlStatus = | ||||
|     'untouched'|'touched'|'pristine'|'dirty'|'valid'|'invalid'|'pending'|'submitted'; | ||||
| type AnyControlStatus = 'untouched'|'touched'|'pristine'|'dirty'|'valid'|'invalid'|'pending'; | ||||
| 
 | ||||
| export class AbstractControlStatus { | ||||
|   private _cd: AbstractControlDirective|null; | ||||
| @ -23,17 +22,6 @@ export class AbstractControlStatus { | ||||
|   } | ||||
| 
 | ||||
|   is(status: AnyControlStatus): boolean { | ||||
|     // Currently with ViewEngine (in AOT mode) it's not possible to use private methods in host
 | ||||
|     // bindings.
 | ||||
|     // TODO: once ViewEngine is removed, this function should be refactored:
 | ||||
|     //  - make the `is` method `protected`, so it's not accessible publicly
 | ||||
|     //  - move the `submitted` status logic to the `NgControlStatusGroup` class
 | ||||
|     //    and make it `private` or `protected` too.
 | ||||
|     if (status === 'submitted') { | ||||
|       // We check for the `submitted` field from `NgForm` and `FormGroupDirective` classes, but
 | ||||
|       // we avoid instanceof checks to prevent non-tree-shakable references to those types.
 | ||||
|       return !!(this._cd as unknown as {submitted: boolean} | null)?.submitted; | ||||
|     } | ||||
|     return !!this._cd?.control?.[status]; | ||||
|   } | ||||
| } | ||||
| @ -48,11 +36,6 @@ export const ngControlStatusHost = { | ||||
|   '[class.ng-pending]': 'is("pending")', | ||||
| }; | ||||
| 
 | ||||
| export const ngGroupStatusHost = { | ||||
|   ...ngControlStatusHost, | ||||
|   '[class.ng-submitted]': 'is("submitted")', | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * @description | ||||
|  * Directive automatically applied to Angular form controls that sets CSS classes | ||||
| @ -86,8 +69,7 @@ export class NgControlStatus extends AbstractControlStatus { | ||||
| /** | ||||
|  * @description | ||||
|  * Directive automatically applied to Angular form groups that sets CSS classes | ||||
|  * based on control status (valid/invalid/dirty/etc). On groups, this includes the additional | ||||
|  * class ng-submitted. | ||||
|  * based on control status (valid/invalid/dirty/etc). | ||||
|  * | ||||
|  * @see `NgControlStatus` | ||||
|  * | ||||
| @ -98,7 +80,7 @@ export class NgControlStatus extends AbstractControlStatus { | ||||
| @Directive({ | ||||
|   selector: | ||||
|       '[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]', | ||||
|   host: ngGroupStatusHost | ||||
|   host: ngControlStatusHost | ||||
| }) | ||||
| export class NgControlStatusGroup extends AbstractControlStatus { | ||||
|   constructor(@Optional() @Self() cd: ControlContainer) { | ||||
|  | ||||
| @ -190,7 +190,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should update nested form group model when UI changes', () => { | ||||
|         const fixture = initTest(NestedFormGroupNameComp); | ||||
|         const fixture = initTest(NestedFormGroupComp); | ||||
|         fixture.componentInstance.form = new FormGroup( | ||||
|             {'signin': new FormGroup({'login': new FormControl(), 'password': new FormControl()})}); | ||||
|         fixture.detectChanges(); | ||||
| @ -242,7 +242,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should pick up dir validators from nested form groups', () => { | ||||
|         const fixture = initTest(NestedFormGroupNameComp, LoginIsEmptyValidator); | ||||
|         const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); | ||||
|         const form = new FormGroup({ | ||||
|           'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) | ||||
|         }); | ||||
| @ -260,7 +260,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should strip named controls that are not found', () => { | ||||
|         const fixture = initTest(NestedFormGroupNameComp, LoginIsEmptyValidator); | ||||
|         const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); | ||||
|         const form = new FormGroup({ | ||||
|           'signin': new FormGroup({'login': new FormControl(''), 'password': new FormControl('')}) | ||||
|         }); | ||||
| @ -335,7 +335,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|         }); | ||||
| 
 | ||||
|         it('should attach dirs to all child controls when group control changes', () => { | ||||
|           const fixture = initTest(NestedFormGroupNameComp, LoginIsEmptyValidator); | ||||
|           const fixture = initTest(NestedFormGroupComp, LoginIsEmptyValidator); | ||||
|           const form = new FormGroup({ | ||||
|             signin: new FormGroup( | ||||
|                 {login: new FormControl('oldLogin'), password: new FormControl('oldPassword')}) | ||||
| @ -1087,8 +1087,6 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         const input = fixture.debugElement.query(By.css('input')).nativeElement; | ||||
|         const formEl = fixture.debugElement.query(By.css('form')).nativeElement; | ||||
| 
 | ||||
|         expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']); | ||||
| 
 | ||||
|         dispatchEvent(input, 'blur'); | ||||
| @ -1101,19 +1099,6 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'submit'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(input)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'reset'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(input)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should work with formGroup', () => { | ||||
| @ -1137,126 +1122,6 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(formEl)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'submit'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(formEl)).toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'reset'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(input)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should not assign `ng-submitted` class to elements with `formArrayName`', () => { | ||||
|         // Since element with the `formArrayName` can not represent top-level forms (can only be
 | ||||
|         // inside other elements), this test verifies that these elements never receive
 | ||||
|         // `ng-submitted` CSS class even when they are located inside submitted form.
 | ||||
|         const fixture = initTest(FormArrayComp); | ||||
|         const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]); | ||||
|         const form = new FormGroup({cities: cityArray}); | ||||
|         fixture.componentInstance.form = form; | ||||
|         fixture.componentInstance.cityArray = cityArray; | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         const [loginInput, passwordInput] = | ||||
|             fixture.debugElement.queryAll(By.css('input')).map(el => el.nativeElement); | ||||
|         const arrEl = fixture.debugElement.query(By.css('div')).nativeElement; | ||||
|         const formEl = fixture.debugElement.query(By.css('form')).nativeElement; | ||||
| 
 | ||||
|         expect(passwordInput).toBeDefined(); | ||||
|         expect(sortedClassList(loginInput)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(arrEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'submit'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(loginInput)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(arrEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'reset'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(loginInput)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(arrEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should apply submitted status with nested formArrayName', () => { | ||||
|         const fixture = initTest(NestedFormArrayNameComp); | ||||
|         const ic = new FormControl('foo'); | ||||
|         const arr = new FormArray([ic]); | ||||
|         const form = new FormGroup({arr}); | ||||
|         fixture.componentInstance.form = form; | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         const input = fixture.debugElement.query(By.css('input')).nativeElement; | ||||
|         const arrEl = fixture.debugElement.query(By.css('div')).nativeElement; | ||||
|         const formEl = fixture.debugElement.query(By.css('form')).nativeElement; | ||||
| 
 | ||||
|         expect(sortedClassList(input)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(arrEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'submit'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(input)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(arrEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'reset'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(input)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(arrEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should apply submitted status with nested formGroupName', () => { | ||||
|         const fixture = initTest(NestedFormGroupNameComp); | ||||
|         const loginControl = | ||||
|             new FormControl('', {validators: Validators.required, updateOn: 'change'}); | ||||
|         const passwordControl = new FormControl('', Validators.required); | ||||
|         const formGroup = new FormGroup( | ||||
|             {signin: new FormGroup({login: loginControl, password: passwordControl})}, | ||||
|             {updateOn: 'blur'}); | ||||
|         fixture.componentInstance.form = formGroup; | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         const [loginInput, passwordInput] = | ||||
|             fixture.debugElement.queryAll(By.css('input')).map(el => el.nativeElement); | ||||
| 
 | ||||
|         const formEl = fixture.debugElement.query(By.css('form')).nativeElement; | ||||
|         const groupEl = fixture.debugElement.query(By.css('div')).nativeElement; | ||||
|         loginInput.value = 'Nancy'; | ||||
|         // Input and blur events, as in a real interaction, cause the form to be touched and
 | ||||
|         // dirtied.
 | ||||
|         dispatchEvent(loginInput, 'input'); | ||||
|         dispatchEvent(loginInput, 'blur'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(loginInput)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(groupEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'submit'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(loginInput)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(groupEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).toContain('ng-submitted'); | ||||
| 
 | ||||
|         dispatchEvent(formEl, 'reset'); | ||||
|         fixture.detectChanges(); | ||||
| 
 | ||||
|         expect(sortedClassList(loginInput)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(groupEl)).not.toContain('ng-submitted'); | ||||
|         expect(sortedClassList(formEl)).not.toContain('ng-submitted'); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
| @ -1636,7 +1501,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
| 
 | ||||
| 
 | ||||
|         it('should allow child control updateOn blur to override group updateOn', () => { | ||||
|           const fixture = initTest(NestedFormGroupNameComp); | ||||
|           const fixture = initTest(NestedFormGroupComp); | ||||
|           const loginControl = | ||||
|               new FormControl('', {validators: Validators.required, updateOn: 'change'}); | ||||
|           const passwordControl = new FormControl('', Validators.required); | ||||
| @ -1945,7 +1810,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|           const validatorSpy = jasmine.createSpy('validator'); | ||||
|           const groupValidatorSpy = jasmine.createSpy('groupValidatorSpy'); | ||||
| 
 | ||||
|           const fixture = initTest(NestedFormGroupNameComp); | ||||
|           const fixture = initTest(NestedFormGroupComp); | ||||
|           const formGroup = new FormGroup({ | ||||
|             signin: new FormGroup({login: new FormControl(), password: new FormControl()}), | ||||
|             email: new FormControl('', {updateOn: 'submit'}) | ||||
| @ -2042,7 +1907,7 @@ const ValueAccessorB = createControlValueAccessor('[cva-b]'); | ||||
|         }); | ||||
| 
 | ||||
|         it('should allow child control updateOn submit to override group updateOn', () => { | ||||
|           const fixture = initTest(NestedFormGroupNameComp); | ||||
|           const fixture = initTest(NestedFormGroupComp); | ||||
|           const loginControl = | ||||
|               new FormControl('', {validators: Validators.required, updateOn: 'change'}); | ||||
|           const passwordControl = new FormControl('', Validators.required); | ||||
| @ -4942,7 +4807,7 @@ class FormGroupComp { | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'nested-form-group-name-comp', | ||||
|   selector: 'nested-form-group-comp', | ||||
|   template: ` | ||||
|     <form [formGroup]="form"> | ||||
|       <div formGroupName="signin" login-is-empty-validator> | ||||
| @ -4952,7 +4817,7 @@ class FormGroupComp { | ||||
|       <input *ngIf="form.contains('email')" formControlName="email"> | ||||
|     </form>` | ||||
| }) | ||||
| class NestedFormGroupNameComp { | ||||
| class NestedFormGroupComp { | ||||
|   // TODO(issue/24571): remove '!'.
 | ||||
|   form!: FormGroup; | ||||
| } | ||||
| @ -4975,20 +4840,6 @@ class FormArrayComp { | ||||
|   cityArray!: FormArray; | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'nested-form-array-name-comp', | ||||
|   template: ` | ||||
|     <form [formGroup]="form"> | ||||
|       <div formArrayName="arr"> | ||||
|         <input formControlName="0"> | ||||
|       </div> | ||||
|     </form> | ||||
|   ` | ||||
| }) | ||||
| class NestedFormArrayNameComp { | ||||
|   form!: FormGroup; | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'form-array-nested-group', | ||||
|   template: ` | ||||
|  | ||||
| @ -187,21 +187,6 @@ 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'); | ||||
|            }); | ||||
|          })); | ||||
| 
 | ||||
| @ -259,52 +244,9 @@ 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(); | ||||
| @ -2425,24 +2367,6 @@ class NgModelNgIfForm { | ||||
|   email!: string; | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ng-model-nested', | ||||
|   template: ` | ||||
|     <form> | ||||
|       <div ngModelGroup="contact-info"> | ||||
|         <input name="first" [(ngModel)]="first"> | ||||
|         <div ngModelGroup="other-names"> | ||||
|           <input name="other-names" [(ngModel)]="other"> | ||||
|         </div> | ||||
|       </div> | ||||
|     </form> | ||||
|   ` | ||||
| }) | ||||
| class NgModelNestedForm { | ||||
|   first!: string; | ||||
|   other!: string; | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ng-no-form', | ||||
|   template: ` | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user