2

I'm trying to write an attribute directive for input elements in my forms which validates the given username if it's available or not.

I have 2 problems doing so.

1) I want to make this directive reusable by allowing it to receive the API route it should be validating usernames with. Here's what I have and as you can see it doesn't accept any input (doesn't have bindings) and it works perfectly.

import { Directive, forwardRef } from '@angular/core';
import { AbstractControl, Validator, NG_ASYNC_VALIDATORS } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../../../environments/environment';
import { Observable } from 'rxjs/Observable';

@Directive({
  selector: '[uniqueCompanyUsername]',
  providers: [
    {
      provide: NG_ASYNC_VALIDATORS,
      useExisting: forwardRef(() => CompanyUsernameValidatorDirective), multi: true
    },
  ]
})
export class CompanyUsernameValidatorDirective implements Validator {
  constructor(private http: HttpClient) {
  }

  validate(username: AbstractControl) {
    return Observable.timer(1000).switchMap(() => {
      return this.http.post(environment.apiEndpoint + '/company/username-check', {
        username: username.value
      }).map((result: any) => result.success ? null : { uniqueCompanyUsername: false });
    });
  }
}

Once I add @Input('uniqueCompanyUsername') route: string; to it and try to take the route via the directive itself it gives me this error:

Can't bind to 'uniqueCompanyUsername' since it isn't a known property of 'input'.

Here's what I wrote in my directive controller:

import { Directive, forwardRef, Input } from '@angular/core';
import { AbstractControl, Validator, NG_ASYNC_VALIDATORS } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../../../environments/environment';
import { Observable } from 'rxjs/Observable';

@Directive({
  selector: '[uniqueCompanyUsername]',
  providers: [
    {
      provide: NG_ASYNC_VALIDATORS,
      useExisting: forwardRef(() => CompanyUsernameValidatorDirective), multi: true
    },
  ]
})
export class CompanyUsernameValidatorDirective implements Validator {
  constructor(private http: HttpClient) {
  }

  @Input('uniqueCompanyUsername') route: string;

  validate(username: AbstractControl) {
    return Observable.timer(1000).switchMap(() => {
      return this.http.post(`${environment.apiEndpoint}${this.route}`, {
        username: username.value
      }).map((result: any) => result.success ? null : { uniqueCompanyUsername: false });
    });
  }
}

Here's my template:

<input name="companyUsername" [(ngModel)]="company.username" id="input-username" #companyUsername="ngModel" class="form-control" placeholder="Company Username"
                [class.form-control-danger]="companyUsername.invalid && companyUsername.touched" [minlength]="3" [maxlength]="50" [required]="true"
                [uniqueCompanyUsername]="/company/username/" autofocus>

I'm frustrated and I don't simply understand what's wrong here!

2
  • 1
    Your directive should work but if you want to pass string then write [uniqueCompanyUsername]="'/company/username/'" or uniqueCompanyUsername="/company/username/" See example stackblitz.com/edit/… Commented Jan 10, 2018 at 18:30
  • @yurzui yeah my bad. Commented Jan 10, 2018 at 19:15

1 Answer 1

2

Your attribute name needs to match your Input decorator name.

Change this:

@Input('uniqueCompanyUsername') route: string;

to this:

@Input() uniqueCompanyUsername: string;

and remember to also change "this.route"s in your component to "this.uniqueCompanyUsername"s.

More info on Avoid aliasing inputs and outputs.

Sign up to request clarification or add additional context in comments.

2 Comments

It works, thanks! But what if I want to still get the route using [uniqeCompanyUsername] and use this.router?? Like what I wrote?
@mamsoudi Please check the added link. Basically, your attribute name needs to match your Input decorator name. If you want to use route, then change [uniqeCompanyUsername] to [route] in your template.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.