Search code examples
angularangular-directive

I am getting undefined for the shared data injected in a directive


So I am trying to inject a shared data into a directive to be used by a validator to validate the email input by the user. My problem is I am getting 'undefined' for the value of my shared data from inside the directive. Here are the snippets:

registration.html

  div class="form-group md-form" [class.has-error]="email.touched && email.dirty && email?.errors?.emailExist">
    <i class="fa fa-envelope prefix grey-text"></i>
    <input type="email" name="email" id="email" email="true" appCheckEmail [(ngModel)]="dealerRegistrationInput.email" #email="ngModel" class="form-control" placeholder="Email Address">
     <div class="help-block alert alert-danger " *ngIf="email.errors && (email.dirty || email.touched) && email?.errors?.emailExist">
     <div [hidden]="!email.errors.email">
      A valid email is required.
     </div>
    <div [hidden]="email?.errors.emailExist">
      This email address already exist
    </div>
   </div>
  </div>

registration.ts

.......
 constructor(
    private router: Router,
    private route:  ActivatedRoute,
    private http: HttpClient,
    private sharedData: SharedDataService,
    private connectService: ConnectService,
    private modalService: BsModalService) { }

  ngOnInit() {
     this.connectService.getAllDealers().subscribe((dealers: Dealer[]) => {
      this.sharedData.dealers  = dealers;
      console.log('Dealers.....' + JSON.stringify(this.sharedData.dealers));
    });
  }
 ............
 .........
 .............

check-mail directive

    import {  Input, Directive, forwardRef } from '@angular/core';
    import { Validator, FormControl, AbstractControl, NG_VALIDATORS, ValidationErrors } from '@angular/forms';
    import { SharedDataService } from './shared-data.service';


    /* ......Functions.........................*/

      function appCheckEmail(dealersEmails: string[]) {
console.log('Dealers..:   ' + JSON.stringify(dealersEmails)); //THIS IS UNDEFINED
           return (control: AbstractControl) => {
  console.log('Dealers email addresses2:   ' + JSON.stringify(dealersEmails));
  console.log('Control value: ' + control.value)
  ...

  ...
      if (dealersEmails.indexOf(control.value) >= 0) {
                   return null;
           } else {
              return{  emailnotExist: true  };
           }
        };
       }

     /* -------------------------End of Functions.............*/

    @Directive({
      selector: '[appCheckEmail][ngModel], [appCheckEmail][ngModel]',
      providers: [{
        provide: NG_VALIDATORS,
        useExisting: forwardRef(() => CheckEmailDirective),
        multi: true
      }]
    })

    export class CheckEmailDirective implements Validator {
      validator: Function;

      constructor(private sharedData: SharedDataService) {

      this.validator = appCheckEmail(sharedData.dealers); /* sharedData.dealers is undefined */
         }
         validate(control: AbstractControl): ValidationErrors | null {
            return this.validator(control);
          }

        }

somehow the shared data in the directive despite the injection is not pointing to the correct reference or the value is cleared. I also observed something very curious ...the checkMail directive is been executed once the html is rendered...before any data entered and apparently on every keystroke of the targeted input field....could this behaviour be causing the problem? I would appreciate any help I can get.

--EDIT-- Having changed the appCheckEmail parameter to appCheckEmail(sharedData:SharedDataService)

and the indexOf search to:

 if (sharedData.dealers.indexOf(control.value) >= 0){....}

The search still fails to find a match. I have confirmed with console.log that the sharedData.dealers array is in fact an array of strings and the control.value is a string with the values I am expecting. I have also tried simple for loop with:

if(sharedData.dealers[i] === control.value) I even tried: if(sharedData.dealers[i] === '[email protected]')

and both fails. I know I am a missing something fundamental here but I can't seem to find it. Could it be that sharedData.dealers[i] is still pointing to the wrong place even though I passed the reference in the constructor?


Solution

  • Instead of passing the dealers to the validate function, you should pass the reference to your service to the function. The problem with your solution is the fact that the dealers doesn't exist in the constructor. You are fetching the dealers in your Component asynchronously, so they can't exist during build time in your Directive constructor

    import {  Input, Directive, forwardRef } from '@angular/core';
    import { Validator, FormControl, AbstractControl, NG_VALIDATORS, ValidationErrors } from '@angular/forms';
    import { SharedDataService } from './shared-data.service';
    
    
    /* ......Functions.........................*/
    
    function appCheckEmail(sharedData: SharedDataService) {
    
      return (control: AbstractControl) => {
        console.log('Dealers email addresses2:   ' + JSON.stringify(sharedData.dealersEmails));
        console.log('Control value: ' + control.value)
        ...
    
        ...
        if (sharedData.dealersEmails.indexOf(control.value) >= 0) {
          return null;
         } else {
          return{  emailnotExist: true  };
        }
      };
    }
    
    /* -------------------------End of Functions.............*/
    
    @Directive({
      selector: '[appCheckEmail][ngModel], [appCheckEmail][ngModel]',
      providers: [{
        provide: NG_VALIDATORS,
        useExisting: forwardRef(() => CheckEmailDirective),
        multi: true
      }]
    })
    export class CheckEmailDirective implements Validator {
      validator: Function;
    
      constructor(private sharedData: SharedDataService) {
        this.validator = appCheckEmail(sharedData); /* sharedData.dealers is undefined */
      }
      
      validate(control: AbstractControl): ValidationErrors | null {
        return this.validator(control);
      }
    
    }