1

I have a directive in Angular that should read it's host element's input selectionStart, selectionEnd values. I do this in my custom directive:

@Directive({
  selector: '[inputBehavior]',
})
export class InputBehaviorDirective {
  public constructor(
    private el: ElementRef
  ) {}

  @HostListener('keydown', ['$event'])
  onKeyDown(event: KeyboardEvent): void {
    let { selectionStart, selectionEnd } = this.el.nativeElement as HTMLInputElement;
  }
}

host element's template looks like this:

<div class="input-field">
    <div class="container">
        <input [formControl]="formElement" />
    </div>
</div>

host's typescript file:

@UntilDestroy()
@Component({
  selector: 'input-field',
  templateUrl: './input-field.component.html',
  styleUrls: ['./input-field.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputFieldComponent),
      multi: true,
    },
  ],
})
export class InputFieldComponent implements ControlValueAccessor {
  public formElement = new FormControl();

  public writeValue(obj: number | string | undefined): void {
    this.formElement.setValue(obj?.toString());
  }

  public registerOnChange(fn: any): void { }
  public registerOnTouched(fn: any): void {}
  public setDisabledState?(isDisabled: boolean): void {}
}

i use directive like this:

<input-field
    InputBehavior
    formControlName="height"
>
</input-field>

but selectionStart and selectionEnd stay undefined. I want to apply the directive to the ControlValueAccessor host element as whole and not to the native html input element within host element's tempalte. So NOT like this:

<div class="input-field">
    <div class="container">
        <input InputBehavior 
               [formControl]="formElement" 
        />
     </div>
</div>

so my question is, how to read selectionStart and selectionEnd values of the host element by applying the directive to it?

1 Answer 1

2

You can "reach" the input using el.nativeElement.getElementsByTag('input')[0]

@HostListener('keydown', ['$event'])
  onKeyDown(event: KeyboardEvent): void {
    const inputElement=this.el.nativeElement.getElementsByTag('input')[0]
    let { selectionStart, selectionEnd } = inputElement as HTMLInputElement;
    console.log(selectionStart, selectionEnd)
  }

Another option can be change your directive in the way

export class InputBehaviorDirective {
  public inputElement!:HTMLInputElement; //<--add a variable public
  public constructor(
    private el: ElementRef
  ) {}

  @HostListener('keydown', ['$event'])
  onKeyDown(event: KeyboardEvent): void {
    //use the variable inputElement
    let { selectionStart, selectionEnd } = this.inputElement as HTMLInputElement;
    console.log(selectionStart, selectionEnd)
  }
}

And your InputFieldComponent like

The template

<div class="input-field">
  <div class="container">
      <!--Add a template reference variable-->
      <input #myinput [formControl]="formElement" />
  </div>
</div>

In your .ts, inject in constructor the directive and use ViewChild with a setter

  @ViewChild('myinput',{static:true}) set _myInput(value:any)
  {
    if (this.behaviorDirective)
         this.behaviorDirective.inputElement=value;
  }

  constructor(@Host() @Optional() private behaviorDirective:InputBehaviorDirective){
  }
Sign up to request clarification or add additional context in comments.

3 Comments

The inputElement can be defined as @Input() inputElement!: HTMLElement; in the directive and then assigned in the template as <input #myinput [formControl]="formElement" inputBehavior [inputElement]="myinput"> and you can skip the part with the @ViewChild setter and the DI injector - which in my case, didn't work: I was correctly setting the inputElement in the setter, but despite this it was undefined when the directive code was reached.
@Rekesoft, see that this directive is because you want to "reach" an inner input tag. To "reach" the own element (the element where is added the directive -as your example-) you can simply inject ElementRef in constructor:constructor(private el:ElementRef){..}. NOTE: To access to the HTMLelement, from this.el, you use this.el.nativeElement
Oh yes, I see, I hadn't read the OP's question too carefully.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.