Andrew Howard
Andrew Howard

Reputation: 3092

Email regex pattern crashes my Reactive form

I have a normal reactive form for an email address which uses regex to check that its an email address in the correct format:

this.fBuilder.group({
                email: [null, [Validators.required, Validators.pattern("[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])+")]],
            });

However we had a rare situation where the back end needed to make up fake email address like this one:

ANS-ANS-ANS-ANS-ANS-ANS-ANS-ANS-xopnqs.xxxxxxxxxxxxxxxxhicken@qhahlaneinu.xxxxxxxxxxxxxxxxco.xxxxxxxxxxxxxxxxukxxxxxxxxxxxxxxxx

And it crashes the page. I know this wouldn't happen in the normal world, but is there a way I can amend the regex without this crashing the page? The moment the email address changes to something normal like [email protected] it's all fine, so I definitely know it's one of those rare situations where the data is crashing the page.

Upvotes: 0

Views: 328

Answers (2)

Ahmet Emrebas
Ahmet Emrebas

Reputation: 944

It is very interesting and valuable question. The problem is NOT about Reactive form validation. It seems the pattern is too complex for the browser to compute.

For such a cpu-intensive computation, we use Web Workers.

Web Worker provides more responsive experience to clients.

Web worker is a standalone process that works background independent from the main thread so the browser does not lock.

Here is the solution for your problem.

validator.worker.ts


/// <reference lib="webworker" />


// web worker class can be generated using by ng-cli
// ng g web-worker


const emailPattern = /[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])+/;

// When the user posts a message to this worker, 
// Worker will process the message payload and send it back to the worker that registered to this worker file.

addEventListener('message', ({ data }) => {
  const response = emailPattern.test(data.payload);
  postMessage({
    type: 'email',
    payload: response,
  });
});

app.component.ts

import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit, OnDestroy {

  worker!: Worker;
  formGroup!: FormGroup;

  ngOnInit() {

    // registering the worker to the validator.worker. 
    this.worker = new Worker('./validator.worker', { type: 'module' });

    const emailControl = new FormControl('', [], (c) => {
      return new Promise((resolve, reject) => {
        let isEmailFieldValid: any = null;
        
        // Adding listener to validator.worker response.
        this.worker.onmessage = ({ data }) => {
          if (data.payload) {
            isEmailFieldValid = null;
            return;
          }
          isEmailFieldValid = { email: 'Email is invalid.' };
        };

        // Posting our first message to the validator.worker.
        // it will post back the validation result which we are listening above.
        this.worker.postMessage({ type: 'email', payload: c.value });

        // we need to wait for the validation result for a while. 
        setTimeout(() => {
          resolve(isEmailFieldValid);
        }, 200);
      });
    });

    this.formGroup = new FormGroup({
      email: emailControl,
    });

    this.formGroup.statusChanges.subscribe((formStatus) => {
      console.log('Form Status: ', formStatus);
    });

    this.formGroup.controls['email'].statusChanges.subscribe((emailStatus) => {
      console.log('Email Status: ', emailStatus);
    });
  }

  ngOnDestroy(): void {
    // Terminating the worker after we are done with it.
    this.worker.terminate();
  }
}

app.component.html


<form [formGroup]="formGroup" #ngForm="ngForm">
  <input
    formControlName="email"
    [class.invalid]="formGroup.controls['email'].invalid && ngForm.submitted"
    [class.valid]="formGroup.controls['email'].valid && ngForm.submitted"
  />
  <button type="submit">Submit</button>
</form>

Upvotes: 0

Kartik Dolas
Kartik Dolas

Reputation: 761

If you're using Angular reactive form then you don't need to write regex for email.
1.Import Validators class

import { FormBuilder, Validators } from '@angular/forms'

Add this part to your formgroup
this.fBuilder.group({
                email: [null, [Validators.required, Validators.email]],
            });

Upvotes: 0

Related Questions