While working with validating same radio input elements with two different approaches viz. template-driven-form and model-driven-form, I am stuck with the scenario where for template-driven-form, using ngModel, I get single instance of control for 3 radio elements but for model-driven-form, using formControlName, I get 3 separate instances.
<!-- template-driven-form.component.html -->
<div class="form-group gender">
<label for="gender">Select Gender:</label>
<div class="radio" *ngFor="let gender of genders">
<input type="radio" name="gender" [value]="gender" ngModel appFormControlValidation validationMsgId="gender" required />
<label>{{ gender }}</label>
</div>
</div>
<!-- model-driven-form.component.html -->
<div class="form-group gender">
<label for="gender">Select Gender:</label>
<div class="radio" *ngFor="let gender of genders">
<input type="radio" name="gender" [value]="gender" formControlName="gender" appFormControlValidation validationMsgId="gender" required />
<label>{{ gender }}</label>
</div>
</div>
// model-driven-form.component.ts
genders: string[] = ['Male', 'Female', 'Other'];
this.modelForm = new FormGroup({
gender: new FormControl(null, [Validators.required])
});
// template-driven-form.component.ts
genders: string[] = ['Male', 'Female', 'Other'];
// form-control-directive
(this.control as NgControl).statusChanges.subscribe(
// returns single instance for 3 radio elements -> template form
// returns 3 instance for 3 radio elements -> model form
);
As from snippet above, I am using same HTML structure for both forms yet number instances vary. Problem here is when the validation happens, for template-driven-form, I get error message only once (which is expected scenario) but for model-driven-form, I get error messages displayed 3 times!
My question is:
- Why is number of instance generated for same element type different for
ngModelandformControlName? - What changes are required so that
formControlNamealso returns single instance?
Working Stackbliz version

Answered by @AndreiGatej - https://github.com/indepth-dev/community/discussions/53
Description:
After a quick investigation, it is actually expected to get three instances. Concretely, the
statusChangesfromthis.statusChangeSubscription = this.control?.statusChanges?.subscribe()is subscribed 3 times. If you haveNradio buttons, then you'll end up withNinstances of that directive. This implies that although you're havingNinstances, they will all inject the sameNgControlinstance(which can beNgModel,FormControlNameandFormControlDirective). And thatNgControlinstance is, in the case of reactive forms, this one:The reason this doesn't apparently happen when using
NgModelis that when using Template-Driven forms, the form control directives are created on the fly. This is the flow that sets up anNgModeldirective:this.formDirective.addControl(this).NgModel'sNgControlis added in the Form Directives Tree at the end of this tick. The reason for that can be found here.NgModeldirective inherently creates a uniqueFormControlinstance, in the directives tree there will be only oneFormControlinstance which will be shared by all theNgModelwhich share the same name. Here is the piece of logic which indicates that a singleFormControlinstance will be shared:registerControlis called after theresolvedPromisepromise has been resolved. It's also there wheredir.controlis assigned to whateverregisterControlreturns.So, the directive's
ngOnInitis called before that promise(which is essentiallyPromise.resolve()) resolves. At the end of the current tick, all the NgModel directives will share the sameFormControlinstance. This is why it works as expected.