Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

check if emails match on blur

I'm trying to check if email field and confirm email field match each other. That is, the user types in their email and then they have to confirm it again. I want the match/validation to happen on blur (when the user presses enter or the textfield loses focus).

Here's my ts file:

import {Component, OnInit} from '@angular/core';
import {User} from './user.interface';
import {FormBuilder, FormGroup, ValidatorFn} from '@angular/forms';

@Component({
    selector: 'my-email',
    templateUrl: '/app/components/profile/email.component.html',
    styleUrls:['styles.css'], 
})

export class EmailComponent implements OnInit {   

   public user : User;
   Form : FormGroup; 

   ngOnInit() {
        // initialize model here
        this.user = {
            Email: '',
            confirmEmail: ''
        }
         if(this.Form.valid) {
            this.displayErrors = false;
         }
    }

     constructor(fb: FormBuilder, private cookieService: CookieService, private router: Router) {
          this.Form = fb.group({
            email: [''],
            confirmEmail: ['']
        },
        {
            validator: this.matchingEmailsValidator('email', 'confirmEmail')
        });


     }

    save(model: User, isValid: boolean) {
        // call API to save customer
        //save email

    }

    matchingEmailsValidator(emailKey: string, confirmEmailKey: string): ValidatorFn {
        return (group: FormGroup): {[key: string]: any} => {

            let email = group.controls[emailKey];
            let confirmEmail = group.controls[confirmEmailKey];

            if (email.value !== confirmEmail.value) {
                return {
                    mismatch: true
                };
            }
        };
    }
}

Here's my view:

  <form [formGroup]="Form" novalidate (ngSubmit)="Form.valid && save(Form.value, Form.valid)">
    <div class="container-fluid">
    <div id = "container" class="contain" style="text-align: center">
        <div> 
            <fieldset class="form-group">
            <label id = "rounded" class="item item-input .col-md-6 .col-md-offset-3">
                <input class="email-address-entry form-control" name="email" type="email" placeholder="[email protected]" formControlName="email" pattern="^(\\w|[0-9.!#$%&’*+/=?^_\`{|}~-])+@(\\w|[0-9-])+(?:‌​[.](\\w|[0-9-])+)*$"/>
            </label>
                <p class="Reenter-your-email">Reenter your email to confirm</p>
            <label id = "rounded" class="item item-input">
                <input class="email-address-entry form-control" (blur)="displayErrors=true" name="confirmEmail" type="email" placeholder="[email protected]" formControlName="confirmEmail" validateEqual="email"/>
            </label> 
            </fieldset>
        </div> 
         <div>
          <label class="entry-invalid">
          <p *ngIf="displayErrors && !Form.get('email').valid">The email you entered does not match.</p>
          </label>
        </div>
        <div (click)="Form.get('email').length > 0 ? save(Form.value, Form.valid) : NaN" class="{{ Form.get('email').length > 0 ? 'container-fluid anchorBottomHighlight' : 'container-fluid anchorBottom'}}">
            <label class="footerLabel">Confirm</label>
        </div>
    </div>
    </div> 
</form>

Currently, with the way it's set up, the validation occurs but it does not get cleared when the correct match is input. I'm wondering how I can setup my view correctly? So the validation message is shown/hidden when the correct match is set and not.

Also it seems like Form.get('email').length > 0 is never greater than 0 / never hit, so my label doesn't toggle to be clickable.

I'm using Angular 2 and reactive forms.

like image 311
Euridice01 Avatar asked Jan 30 '17 06:01

Euridice01


1 Answers

It looks that you're mixing the two form syntaxes: template-driven forms and model-driven forms.

Since you're declaring a form model in your class with FormBuilder, I'm assuming you want a model-driven form.

This means your fields don't need attributes like [(ngModel)] or #EmailAddress.

Instead of that:

<input type="email" [(ngModel)]="user.EmailAddress"  required #EmailAddress="ngModel">

Write this:

<!-- Now I'm using `formControlName` to bind the field to the model -->
<!-- Its value must match one of the names you used in the FormBuilder -->
<input type="email" formControlName="email">

ALL of your validators must be declared in the FormBuilder. Not just matchingEmailsValidator, but also required:

this.Form = fb.group({
  email: ['', Validators.required],
  confirmEmail: ['', Validators.required]
},
{
  validator: this.matchingEmailsValidator('email', 'confirmEmail')
});

Now you can access a field with the following syntax:

// In the class
this.Form.get('email').value
this.Form.get('email').errors
<!-- In the template -->
{{ Form.get('email').value }}
{{ Form.get('email').errors }}

You can use these syntaxes to display errors. For example:

<input type="email" formControlName="email">
<p *ngIf="Form.get('email').dirty && Form.get('email').errors.required">
  This field is required.
</p>

In the example above, I am displaying an error message if the email field has been touched (i.e. the user tried to enter something) AND the required error is present.

You can also verify that your validation rules are enforced by inspecting the form's markup with your browser's dev tools. Angular should have added classes like .ng-invalid or .ng-valid to the <input> tags that have validation rules.

Finally, regarding your question to check email match on blur. You can't postpone Angular's validation, it will happen in real-time (as the user types). But you could wait for the blur event to display errors.

Combining this last advice with my previous example, here's how you could should an error if the email field is empty AND it has lost focus (blur event):

<input type="email" formControlName="email" (blur)="displayErrors=true">
<p *ngIf="displayErrors && Form.get('email').dirty && Form.get('email').errors.required">
  This field is required.
</p>

UPDATE(01-FEB-2017) after Euridice posted this Plunkr:

  • You still have wayyyyy to much validation code in your template. Like I said, ALL VALIDATORS should be declared IN THE FORM MODEL (with the FormBuilder). More specifically:
    • The pattern="..." attribute in the email field should be replaced with Validators.pattern() in the form model.
    • What is the validateEqual="email" attribute in the confirmEmail field? You're not using that anywhere.
  • The main problem is your test to display the error message: *ngIf="displayErrors && !Form.get('email').valid && Form.get('email').error.mismatch".
    • First of all, the property is errors with an "s", not error.
    • Also, your custom validator is setting the error on the form itself, NOT on the email field. This means you should retrieve your mismatch custom error from Form.errors.mismatch, NOT Form.get('email').errors.mismatch.

Here's the updated, working Plunkr: https://plnkr.co/edit/dTjcqlm6rZQxA7E0yZLa?p=preview

like image 182
AngularChef Avatar answered Sep 20 '22 16:09

AngularChef