I would like to initialize a Template-Driven Form with query parameter values.
Intuitively you would create the form and populate it on ngAfterViewInit
:
HTML
<form #f="ngForm">
<input type="text" id="firstName" name="fname" #fname ngModel>
<input *ngIf="fname.value" type="text" id="lastName" name="lname" ngModel>
<button type="submit">Submit</button>
</form>
Component:
@ViewChild('f') f: NgForm;
constructor(private route: ActivatedRoute) {}
ngAfterViewInit() {
const queryParams = this.route.snapshot.queryParams;
this.f.form.setValue(queryParams)
}
then access it with query parameters: ?fname=aaa&lname=bbb
now, there are two issues with this approach:
setValue
won't work because the second ctrl, lname
doesnt exist at the time of applying the values.this will require me to
patchValue
that only applies valid values, twice.something like:
ngAfterViewInit() {
const queryParams = { fname: 'aaa', lname: 'bbb'};
// if we wish to access template driven form, we need to wait an extra tick for form registration.
// angular suggests using setTimeout or such - switched it to timer operator instead.
timer(1)
// since last name ctrl is only shown when first name has value (*ngIf="fname.value"),
// patchValue won't patch it on the first 'run' because it doesnt exist yet.
// so we need to do it twice.
.pipe(repeat(2))
// we use patchValue and not setValue because of the above reason.
// setValue applies the whole value, while patch only applies controls that exists on the form.
// and since, last name doesnt exist at first, it requires us to use patch. twice.
.subscribe(() => this.f.form.patchValue(queryParams))
}
Is there a less hacky way to accomplish this Without creating a variable for each control on the component side as doing that, would, in my opinion, make template driven redundant.
attached: stackblitz Demo of the "hacky" soultion
with [(ngModel)] can try the below
<form #heroForm="ngForm">
<div class="form-group">
<label for="fname">First Name</label>
<input type="text" class="form-control" name="fname" [(ngModel)]="queryParams.fname" required>
</div>
<div class="form-group" *ngIf="queryParams?.fname">
<label for="lname">Last Name</label>
<input type="text" class="form-control" name="lname" [(ngModel)]="queryParams.lname">
</div>
<button type="submit" class="btn btn-success">Submit</button>
Then in form component
export class HeroFormComponent implements OnInit {
@ViewChild("heroForm", null) heroForm: NgForm;
queryParams={};
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.queryParams = { fname: "aaa", lname: "bbb" };
}
}
You no need to declare for each form control. just assign queryParams & ngModel will handle the rest.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With