TL,DR;
Undefined values behave differently depending on the method of achieving two-way binding in Angular Reactive Forms. Please see this StackBlitz for a demo.
Detail
The SO Question, How to correctly 2-way-bind with reactive forms?, received an answer stating...
there's no reason to not mix formControlName and ngModel
Firstly, is this approach a valid way to achieve two-way data binding?
Secondly, can someone clarify the difference in behaviour of undefined values in Angular Reactive Forms? I've created a StackBlitz for this question.
The main HTML form mutates the component's underlying data, attributes
:
export class AppComponent implements OnInit {
public form: FormGroup;
// title and subtitle will be undefined.
public attributes = {};
constructor(private readonly fb: FormBuilder) { }
public ngOnInit(): void {
// This is OK, I want title and subtitle to be undefined.
this.form = this.fb.group({
title: this.attributes.title,
subtitle: this.attributes.subtitle
});
}
}
The form: FormGroup
defines two <input>
and <button>
pairs; one for attributes.title
, another for attributes.subtitle
.
The first <input>
uses [(ngModel)]
- binding to the underlying model with a template driven approach within reactive form:
<!-- Title uses [(ngModel)] for 2-way binding. -->
<input formControlName="title"
[(ngModel)]="attributes.title">
<button (click)="undefineTitle()">Undefine title</button>
The <button>
simply sets attributes.title
to undefined:
public undefineTitle() {
this.attributes.title = undefined;
}
The second <input>
uses [value]
and (input)
rather than [(ngModel)]
for data-binding:
<!-- Subtitle uses [value] and (input) for 2-way binding. -->
<input formControlName="subtitle"
[value]="getValue()"
(input)="setValue($event.target.value)">
<button (click)="undefineSubtitle()">Undefine subtitle</button>
The <button>
simply sets attributes.subtitle
to undefined:
public undefineSubtitle() {
this.attributes.subtitle = undefined;
}
Since attributes.subtitle
is modified using [value]
and (input)
rather than [(ngModel)]
, two simple event handlers are defined in the component:
/* Handlers for 2-way binding of subtitle */
public getValue(): string {
return this.attributes.subtitle;
}
public setValue(s: string) {
this.attributes.subtitle = s;
}
The <input>
element shows undefined
for subtitle, whereas title seems to handler this better (IMO) and displays nothing?
Upon page-load, you can see the problem immediately since neither title
nor subtitle
are defined when the form: FormGroup
is created:
As expected, two-way data-binding works:
...and empty values behave the same: