joeSmith
joeSmith

Reputation: 1605

angular - custom validator with http call

I have recreated the issue here

https://stackblitz.com/edit/angular-custom-validator-issue

You can fork the setup (top right) and then edit as you please

Basically I am trying to create a custom validator in my reactive form setup that takes the value of the form input and sends off an http request to the Datamuse API that counts the number of syllables within a string. Then depending on the response result I return either an error or success. I have followed the angular docs but my solution is having problems with the http call, which I beleive is something to do with the call stack order. So I need help on how I can achieve this, I think it will help me alot in understanding Angular a bit better.

app.component

import { Component, ElementRef } from '@angular/core';
import { FormControl, FormBuilder, FormGroup, Validators, AbstractControl } from '@angular/forms';
import { VERSION } from '@angular/material';
import { SyllableCountValidator } from './syllable-count.service';

@Component({
  selector: 'material-app',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  version = VERSION;
  public poemForm: FormGroup;

  constructor(private fb: FormBuilder, private scv: SyllableCountValidator) {
    this.createForm();
  }

  //ERRORS APPEAR IN CONSOLE after INPUT into 'input text' in the dom

  // 1. create form group with entry inputText. Initialise it with no value 
  // and set the validation to a custom validator called 'count' that is 
  // located in the 'private scv: SyllableCountValidator' service
  // go to syllable-count.service file to see next steps
  createForm() {
    this.poemForm = this.fb.group({
          inputText: ['', [this.scv.count]],
      });
  }
}

syllable-count.service

    import { ValidatorFn, AbstractControl, FormControl } from '@angular/forms';
    import { forEach } from '@angular/router/src/utils/collection';
    import { Injectable } from '@angular/core';
    import { HttpClient, HttpResponse } from '@angular/common/http';
    import { Observable } from 'rxjs';

    @Injectable()
    export class SyllableCountValidator {

        constructor(private http: HttpClient) {}

        // 2. create http request to datamuse API that returns a syllable 
        // count repsonse for requested text
        public requestCount(text): Observable<Object> {
            return this.http.get(`https://api.datamuse.com/words?sp=${text}&md=s`) as Observable<Object>;
        }

        // 3. on any input into the formControl 'inputText' this will fire
        public count(control: FormControl) {

          // 4. split the string received from the fromControl 
          // 'textInput' into an array
          const arrayOfStrings = control.value.split(' ');

          // 5. define empty text variable
          let text = '';

          // 6. loop through step 3. arrayOfStrings and concatonate into 
          // correct format for http call and push to text variable in step 4.
          const pushToText = Array.prototype.forEach.call(arrayOfStrings, function(word) {
              text = `${text}+${word}`;
          });

          // 7. trim first character of text variable in step 4. to remove
          // unessecary '+' character
          const trimmedText = text.substr(1);

          // 8. call requestCount() in step 2. and return validation result
          if (trimmedText) {
            const validateSyllables = this.requestCount(trimmedText).subscribe((response) => {
              // 8.1.1 if syllable count is greater than 5 return error
              // of number of syllables it is greater by
              if ( response[0].numSyllables > 5 ) {
                return {
                  error: 5 - response[0].numSyllables
                }
              // 8.1.2 if syllable count is less than 5 return error
              // of number of syllables it is less by
              } else if ( response[0].numSyllables > 5 ) {
                return {
                  error: 5 - response[0].numSyllables
                }
              // 8.1.3 if syllable count equals 5 return validation
              // success as required by angular of 'null'
              } else if ( response[0].numSyllables == 5 ) {
                return null;
              }
            });

            // 9.2. return validation result
            return validateSyllables;      
          }
      }
    }

Upvotes: 0

Views: 6408

Answers (1)

Sergey Rudenko
Sergey Rudenko

Reputation: 9235

You have issue with 'this' that is not pointing to the proper scope.

"Easy" fix is to use fat arrow function in your service provider this way to ensure 'this' is pointing at the service provider:

instead of:

public count(control: FormControl) {

do:

public count = (control: FormControl) => {

Now your code still needs other issues addressed (like parsing of the response object is not valid).

Upvotes: 3

Related Questions