Angular 2 form validation framework implementation not working

376 views Asked by At

I am trying to implement a form validation pattern based off of this article: https://coryrylan.com/blog/angular-form-builder-and-validation-management

The validation itself is working fine, but the component to display the validation messages never gets triggered. It is instantiated as the constructor gets hit, but "this.control" (which references the input value provided to the component) is undefined.

This is the code for the component itself

import { Component, Input } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { FormValidationService } from "./validation/formValidation.service";

@Component({
    selector: 'form-control-messages',
    template: `<div *ngIf="errorMessage !== null">{{errorMessage}}</div>`
})
export class FormControlMessagesComponent {
    @Input() control: FormControl;

    constructor(){ }

    get errorMessages() {
        for (let propertyName in this.control.errors) {
            if(this.control.errors.hasOwnProperty(propertyName) && this.control.touched){
                return FormValidationService.getValidatorErrorMessage(propertyName, this.control.errors[propertyName]);
            }
        }
        return null;
    }
}

.. the code for the validation service ..

export class FormValidationService {
    static getValidatorErrorMessage(validatorName: string, validatorValue?: any) {
        let messages = {
            'required': 'This field is required.',
            'invalidEmailAddress': 'This is not a valid email address.',
            'minlength': `This must contain at least ${validatorValue.requiredLength} characters.`,
            'invalidStateCode': 'This is not a valid state.',
            'invalidPostalCode': 'This is not a valid postal code.',
        };

        return messages[validatorName];
    }

    // implementing a couple basic validations, again these can be segmented as needed
    static emailValidtor(control){
        // RFC 2822 compliant regex
        if (control.value.match(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/)) {
            return null;
        } 
        else {
            return { 'invalidEmailAddress': true };
        }
    }

    static stateCodeValidator(control){
        let validCodes = ['AK','AL','AR','AZ','CA','CO','CT','DC','DE','FL','GA','GU','HI','IA','ID', 'IL','IN','KS','KY','LA','MA','MD','ME','MH','MI','MN','MO','MS','MT','NC','ND','NE','NH','NJ','NM','NV','NY', 'OH','OK','OR','PA','PR','PW','RI','SC','SD','TN','TX','UT','VA','VI','VT','WA','WI','WV','WY'];
        return (validCodes.indexOf(control.value) !== -1) ? null : { 'invalidStateCode' : true };
    }

    static postalCodeValidator(control){
        console.log('validating postal code');
        // note this will only match US five- and nine-digit codes
        if(control.value.match(/^[0-9]{5}(?:-[0-9]{4})?$/)) {
            return null;
        }
        else {
            return { 'invalidPostalCode': true };
        }
    }
}

and finally, the test form component and template..

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FormValidationService } from '../shared/validation/formValidation.service';

@Component({
  selector: 'test-form',
  templateUrl: 'testForm.component.html'
})
export class TestFormComponent {  
    testForm: FormGroup;

    constructor(private fb: FormBuilder) {
        this.testForm = this.fb.group({
            setup: this.fb.group({
                keyIdentifier: ['', [Validators.required, Validators.minLength(6)]],
                keyType: [''],
            }),
            contactInformation: this.fb.group({
                name: ['', Validators.required],
                emailAddress: ['', [Validators.required, FormValidationService.emailValidator]],
                postalCode: ['', [Validators.required, FormValidationService.postalCodeValidator]]
            })
        });
    }

    save(){
        if(this.testForm.dirty && this.testForm.valid) {
      // DO STUFF
        }
    }
}

<form [formGroup]="testForm" (submit)="save()">
  <div formGroupName="setup">
    <label for="keyIdentifier">Key ID:</label>
    <input type="text" formControlName="keyIdentifier" id="keyIdentifier" />
    <form-control-messages [control]="testForm.controls.setup.controls.keyIdentifier"></form-control-messages>

    <label for="keyType">Key ID:</label>
    <input type="text" formControlName="keyType" id="keyType" />
    <form-control-messages [control]="testForm.controls.setup.controls.keyType"></form-control-messages>
  </div>
  <div formGroupName="contactInformation">
    <label for="name">Name:</label>
    <input type="text" formControlName="name" id="name" />
    <form-control-messages [control]="testForm.controls.contactInformation.controls.name"></form-control-messages>

    <label for="email">Email:</label>
    <input type="email" formControlName="email" id="email" />
    <form-control-messages [control]="testForm.controls.contactInformation.controls.email"></form-control-messages>

    <label for="postalCode">Postal Code:</label>
    <input type="text" formControlName="postalCode" id="postalCode" />
    <form-control-messages [control]="testForm.controls.contactInformation.controls.postalCode"></form-control-messages>
  </div>

  <button type="submit">Save</button>
</form>

I have posted all this up at http://plnkr.co/edit/rxvBLr5XtTdEUy5nGtAG

I would appreciate any input into what I'm missing here.

Thanks in advance!

1

There are 1 answers

0
Jordan Steinberg On

I got it figured out, turned out to be a typo with the plurality of the errorMessage(s) reference in the template for the component. After looking fresh this morning, I finally saw it.

Thanks for anyone who had a look!