What could be causing the error message "Unable to access 'http' property of undefined" to appear in this validator?

Can someone help me with creating an asynchronous validator for a reactive form control that checks if a username already exists? Below is the code for the async validator:

userdata.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserdataService {

   private apiUrl = 'http://apiurl.com/api'; // not the actual URL

   constructor(private http: HttpClient) {}


   checkUsername(control: FormControl): Promise<any> | Observable<any> {

    let isUsernameValid;
    return new Promise<any>(
      (resolve, reject) => {
        this.http.get(this.apiUrl + '/users?name='+control.value).subscribe(
          response => {
            isUsernameValid = response;
        });
        if (isUsernameValid === 'false') {
          resolve({'usernameIsInvalid': true})
        } else {
          resolve(null);
        }
      }
    );
  }

}

Trying to use this validator results in the error: "core.js:4197 ERROR TypeError: Cannot read property 'http' of undefined"

I suspect the issue has to do with the usage of 'this', but I'm unsure why it's not working... To troubleshoot, I inserted a

console.log(this.apiUrl)

within the function, outside the promise block, just to test, and encountered the same error: "core.js:4197 ERROR TypeError: Cannot read property 'apiUrl' of undefined"...

If anyone can shed light on what I might be doing incorrectly and how to resolve it, I would greatly appreciate it.

EDIT:

In my reactive form ts file, I call my service as shown below:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { CustomValidatorsService } from '../services/custom-validators.service';
import { LocationService } from '../services/location.service';
import { UserdataService } from '../services/userdata.service';

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

  userdataForm: FormGroup;
  provinces: any = null;
  provincesLoading = false;
  cities: any = null;
  citiesLoading = false;

  constructor(
    private locationService: LocationService,
    private userdataService: UserdataService,
    private customValidators: CustomValidatorsService,
    private router: Router,
    private route: ActivatedRoute
    ) { }

  ngOnInit(): void {
    this.formInit();
    this.loadProvinces();
  }

  formInit() {

    let dni: number = null;
    let firstname: string = null;
    let lastname: string = null;
    let email: string = null;
    let mobile: number = null;
    let phone: number = null;
    let birthdate: Date = null;
    let username: string = null;
    let password: string = null;

    this.userdataForm = new FormGroup({

        // ... many controls before ...

        'username': new FormControl(username, [
          Validators.required,
          Validators.minLength(3),
          Validators.maxLength(30),
          Validators.pattern(/^[a-zA-ZÀ-ÿ\u00f1\u00d1]+(\s*[a-zA-ZÀ-ÿ\u00f1\u00d1]*)*[a-zA-ZÀ-ÿ\u00f1\u00d1]+$/)
        ], this.userdataService.checkUsername), // <-- async validator here

        // ... form continues...

  }

  loadProvinces() {
    this.provincesLoading = true;
    this.locationService.getProvinces().subscribe(response => {
      this.provinces = response;
      this.provincesLoading = false;
    });
  }

Answer №1

If you're looking to create a custom validator in Angular, it's important that your validator implements the AsyncValidator interface:

import { AbstractControl, AsyncValidator, ValidationErrors } from '@angular/forms';
import { catchError, map } from 'rxjs/operators';
// ...

@Injectable({
  providedIn: 'root'
})
export class UserDataValidator implements AsyncValidator {

  private apiUrl = 'http://apiurl.com/api'; // this is not the actual URL for privacy reasons

  constructor(private http: HttpClient) {}

  validate = (control: AbstractControl) => {
    return this.http.get(`${this.apiUrl}/users?name=${control.value}`).pipe(
      map(isUsernameValid => (isUsernameValid === 'false' ? { usernameIsInvalid: true } : null),
      catchError(() => of(null))
    );
  }

}

To use this custom validator in your FormGroup, you can do the following:

constructor (private userDataValidator: UserDataValidator) {
  this.userdataForm = new FormGroup({
    username: new FormControl(username, [
      // ... other validators
      // Validator classes are currently not functioning properly - refer to
      // https://github.com/angular/angular/issues/24981
      this.userDataValidator.validate
    ]),
    // ... other form controls
}
// ...

Additional Notes


Recommended Resources

Answer №2

If you're looking to implement a validator function that takes a service instance as an argument, check out this helpful article: https://medium.com/@tomaszsochacki/how-to-do-asynchronous-validator-in-angular-7-6e80243a874a

When defining the method like below:

'username': new FormControl(username, [
          Validators.required,
          Validators.minLength(3),
          Validators.maxLength(30),
          Validators.pattern(/^[a-zA-ZÀ-ÿ\u00f1\u00d1]+(\s*[a-zA-ZÀ-ÿ\u00f1\u00d1]*)*[a-zA-ZÀ-ÿ\u00f1\u00d1]+$/)
        ], this.userdataService.checkUsername),

The context of 'this' in the checkUsername method may be missing.

You can alternatively use this approach:

'username': new FormControl(username, [
          Validators.required,
          Validators.minLength(3),
          Validators.maxLength(30),
          Validators.pattern(/^[a-zA-ZÀ-ÿ\u00f1\u00d1]+(\s*[a-zA-ZÀ-ÿ\u00f1\u00d1]*)*[a-zA-ZÀ-ÿ\u00f1\u00d1]+$/)
        ], this.userdataService.checkUsername.bind(this.userdataService)),

By using bind in the first argument, you ensure that the correct 'this' context is passed to the method when it's called.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering errors during the installation of packages using npm

Can someone please assist me with fixing these errors? I am a beginner in the world of web development and encountered this issue while working with react.js and setting up lite-server. Any guidance on how to resolve it would be greatly appreciated. ...

The functionality of the AngularJS nested router is not functioning properly

I am encountering some challenges with Angular routing, specifically when using nested routes: The current 'abc' state works flawlessly for the route /admin/team/:teamId .state('admin', { url: '/admin', controller: & ...

Creating customizable form fields based on user input in Laravel - here's how!

I am feeling a bit lost when trying to generate dynamic fields based on user input. For example, this field is where the user can enter how many fields they want to create: {!! Form::open(array('url' => 'home')) !!} <div clas ...

Understanding how to use TypeScript modules with system exports in the browser using systemjs

I’m facing an issue while using systemjs. I compiled this code with tsc and exported it: https://github.com/quantumjs/solar-popup/blob/master/package.json#L10 { "compilerOptions": { "module": "system", "target": "es5", "sourceMap": true, ...

Form featuring a mandatory checkbox that must be selected in order to proceed; failure to do so will result in an

So here’s the situation: I have a form with a checkbox for agreeing to the terms of service, and I want to make sure it is checked before proceeding with the donation process. I only have the HTML code and no idea how to implement this functionality. Ide ...

Accessing JavaScript elements from PHP code

Is it possible to recognize JavaScript elements if they are nested within PHP tags? For example: <?php $username = "maniacal"; echo "<p class='welcome' id='greeting'>Hi, Welcome to the site!!</p>" ?> That's ...

Issue encountered with dynamic ngStyle variable: ERROR Error: Unable to locate a supporting object 'ngStyleSmall'

I have defined two variables for ngstyle ngStyleSmall = { width: '150px !important', 'max-width': '150px', }; ngStylemedium = { width: '250px !important', 'max-width&apo ...

Explanation on How to utilize the $( document ).ready() jQuery function within the ngAfterViewInit() on a Component class using Angular 2

This is the code snippet: constructor(private el: ElementRef) { } ngAfterViewInit() { this.loadScript('app/homepage/template-scripts.js'); } ...

What is the process for generating an auto-incrementing button with a *different input field*?

After searching everywhere for a code snippet to add another input bootstrap input group, I finally decided to take matters into my own hands and create one from scratch. And would you believe it worked like a charm! ...

Best practices for sorting a value while iterating through a map in ReactJS

Currently working on learning React JS by replicating a PHP website I previously developed. I've successfully used Axios to fetch data from the database and display it on the frontend. Now, I'm facing a challenge where I need to sort the results ...

Guide to showcasing multiple paths of Firebase data on a single Angular page

I am working with a basic database structure that includes information about groups, events, and users. Here is an example: { "groups": { "123": { "name": "developers", "users": { "1": true }, "users_count": 1 } ...

The attempted execution of 'toDataURL' on 'HTMLCanvasElement' resulted in tainted canvases

When I attempt to retrieve the base64 string of an image loaded in the <img> tag, my code works perfectly for locally stored images but not for images sourced from online URLs. I typically fetch my images from Firebase. Here is the HTML: <ion-sl ...

Starting the Android System Magnifier with a Click: A Step-by-Step Guide

Is there a way to incorporate a magnifying glass into an HTML page using jQuery or within an Android app? Ideally, it would be for a single picture. In my application, I am displaying an HTML file from my assets in a webview. The HTML page utilizes jQuery ...

What is the best way to transfer information between two components in React.js by simply clicking a button?

One of the challenges I am currently facing involves rendering data from the Pokemon API in a table. Within this table, there is a button that allows users to select specific data when pressed. My goal is to send this selected data from the table component ...

The JSON.Parse function in Google Apps Script is leading to a 'server connection error' message during debugging

I'm not a professional developer, but I do code occasionally. Currently, I am working on some Apps Script code to interact with an API and store the data in SQL. Most of it is working fine, but I have encountered an issue while debugging in the Apps S ...

Angular 6 - Consistently returning a value of -1

I'm facing an issue with updating a record in my service where the changes are not being reflected in the component's data. listData contains all the necessary information. All variables have relevant data stored in them. For example: 1, 1, my ...

I am experiencing an issue with Array.some not functioning properly within my React component, whereas Array.map is working

I am attempting to utilize Array.prototype.some() within my React component to check if a particular value exists in my array of objects. However, I keep encountering the error message data.some(...) is not a function. Interestingly, Array.prototype.map() ...

What is the best way to utilize resolve and promises in Angular2 to ensure that data is loaded from the server before rendering a page in the

I am currently developing an Angular 6 application with the requirement to load dynamic routes from a database. To achieve this, I have created a service class called DynamicRoutingService which is responsible for loading both static and dynamic routes fro ...

Strange data being received by Angular from ASP.NET API

I'm encountering some trouble with my ASP.NET API communicating with Angular, and I'm unsure of the root cause. I'm seeking assistance in reviewing my code for any glaring errors. Here is a snippet from my API Controller: // Data Structure ...

Displaying the output of a PHP script in an AJAX request

I am trying to display feedback from certain checks, like checking if a file already exists, after making an ajax call to upload a file using my script. However, for some reason, I can't get the response to show up. Why is the response not appearing? ...