What I have:
I am creating an ionic 2 application and have created an angular 2 core component containing
I will refer to this as my input component
I have a page component with a form on it and currently have text inputs. 1 regular input (password) and 1 input wrapped in my input component (username).
this is the relevant part of my page component
ngOnInit() { this.loginForm = this.formBuilder.group({ username: ['', Validators.required], password: ['', Validators.required] }); }
This is a page component template.
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()"> <aw-input-text id="username" name="Username" [formInput]="loginForm.controls.username"></aw-input-text> <ion-item [class.error]="loginForm.controls.password.errors"> <ion-label floating>Password</ion-label> <ion-input type="text" value="" name="password" formControlName="password"></ion-input> <p *ngIf="loginForm.controls.password.errors">This field is required!</p> </ion-item> <button type="submit" class="custom-button" [disabled]="!loginForm.valid" block>Login</button> </form>
This is the template for my input component.
<form [formGroup]="formGroup"> <ion-item> <ion-label floating>{{inputName}}</ion-label> <ion-input type="text" formControlName="inputValue"></ion-input> <p *ngIf="!formGroup.controls.inputValue.valid">This field is required!</p> </ion-item> </form>
and this is the input component
import {Component, Input} from '@angular/core'; import {FormBuilder} from '@angular/forms'; @Component({ selector: 'aw-input-text', templateUrl: 'build/shared/aw-input-text/aw-input-text.html' }) export class AwInputText { @Input('formInput') public formInput; @Input('name') public inputName; public formGroup; constructor(private formBuilder: FormBuilder) { } ngOnInit() { this.formGroup = this.formBuilder.group({ inputValue: this.formInput }); } }
The component displays correctly.
Problem:
Entrance inside the component does not update the actual state of the form in which it is located.
When I fill in the username and then the password, the form becomes valid.
When I fill out the password, the form username remains invalid
Thus, the form can see the actual state of the input component, but only that the input component that changes the actual state does not start the updated form.
Possible Solution 1
As described in this article and plunk
https://scotch.io/tutorials/how-to-build-nested-model-driven-forms-in-angular-2
https://plnkr.co/edit/clTbNP7MHBbBbrUp20vr?p=preview
I could modify my page component to create a form group containing a nested form group for each form control that I want to use inside my input component
ngOnInit() { this.loginForm = this.formBuilder.group({ username: this.formBuilder.group({ username: ['', Validators.required], }), password: ['', Validators.required], }); }
This solution is suitable for the scenario in the article where they add an array of input controls, but in my case, I find it seems hacky
Possible Solution 2
Another hacker solution that I examined uses the @output directive from my input component to trigger an event on the page component, which updates the form whenever the input component is updated.
Input Component Update
this.formGroup.controls.inputValue.valueChanges.subscribe(value => { this.formUpdated.emit({ value: value }) });
Refresh Page Component
public onUpdated(value){ this.loginForm.updateValueAndValidity(); }
and update the page component template
<aw-input-text id="username" name="Username" (updated)="onUpdated($event)" [formInput]="loginForm.controls.username"></aw-input-text>
This gives me the desired functionality, but I think it also seems a bit hacky having an event handler on every page of the form to make the input component work.
Question
Is there a way to get my component to update the correct state of the form it is in (bearing in mind that I would like to reuse this component several times in each form) without resorting to the solution described above.