4
votes

I'm implementing a signup page with Angular 7 and Angular Material. In this page I use a password input component which gets a reference to the used FormGroup. The outer component handles the logical parts (f.e. how the FormGroup looks like, how the FormControls are defined and how each input is validated). When I click into the password input and click somewhere else/enter anything invalid, the error message of the input is shown correctly. But when I click on the submit-button without touching any input fields, the FormControl of the FormGroup are validated (the FormControl even says that it has an error) but not touched, so no error messages are shown.

I've already looked up how to solve this behaviour. Some suggested to manually set all form controls as touched after clicking on the submit-button. Yes, this works but I would like to have a general solution so I don't have to do this for every form I have in the future.

signup.component.ts:

export class SignupComponent implements OnInit {

    signupForm: FormGroup;

    constructor() { }

    ngOnInit() {
        this.signupForm = new FormGroup({
            'password': new FormControl('', [Validators.required]),
            'contact': new FormControl('', [Validators.required])
        });
    }

    onSubmit() {
        console.log('Form validity: ' + this.signupForm.valid);
    }

}

signup.component.html:

<mat-card class="signup-card">
    <h1 class="signup-title">Sign up</h1>

    <form [formGroup]="signupForm" #form="ngForm" (ngSubmit)="onSubmit()" fxLayout="column">
        <!-- Password -->
        <app-base-password-input [parentForm]="signupForm"></app-base-password-input>

        <!-- Contact -->
        <div class="signup-contact">
            <mat-checkbox formControlName="contact" required>We may contact you.</mat-checkbox>
            <mat-error *ngIf="form.submitted && signupForm.controls['contact'].hasError('required')">Please check this box.</mat-error>
        </div>

    <button class="signup-button" mat-raised-button color="primary" type="submit">Sign up</button>

    </form>
</mat-card>

app-base-password-input.component.ts:

export class BasePasswordInputComponent {

    @Input() parentForm: FormGroup;

    constructor() { }

}

app-base-password-input.component.html:

<mat-form-field [formGroup]="parentForm">
    <mat-label>Password</mat-label>
    <input matInput type="password" formControlName="password" required>
    <mat-error *ngIf="parentForm.controls['password'].hasError('required')">Password is required.</mat-error>
</mat-form-field>

What I expect:
When I click on the submit-button without touching/clicking on any input fields, every input shows an error message (f.e. Hey, user! I'm required!).

What actually happens:
When I click on the submit-button without touching/clicking on any input fields, only the FormControls defined in the outer component show error messages (this case: contact checkbox). My input components show no error messages (this case: password component).

I hope I posted everything you needed to answer my question. If something is still unclear, I'll update the post :)

1

1 Answers

0
votes

The only way I know to do it is, as you say, setting every FormControl as "touched" manually in the onSubmit() method.

SignupComponent.ts

onSubmit(){
    console.log('Form validity: ' + this.signupForm.valid);
    touchAllElements(this.signupForm);
}

touchAllElements(formGroup:FormGroup){
    Object.keys(formGroup["controls"]).forEach(element => {
        formGroup.get(element).markAsTouched();
    });
}

To avoid repeat this method in every component with a form, you could move touchAllElements to a service