0

I ask myself if it possible to intercept an ngModel binding with an Angular directive.

For example:

template:

<input type="string" [(ngModel)]="numberVal" name="someName" appNumberInput>

component:

public numberVal: number;

directive:

selector: '[appNumberInput]'
// magic happens to convert a number to string and vice versa.

This problems comes up because it is not possible to use selectionStart and selectionEnd with `<input type="number>" (Get Cursor Position With Input type number and selectionStart/selectionEnd on input type="number" no longer allowed in Chrome).

I know, I could create a custom component which wraps a native input component. However, I wonder if this is solvable with only a custom directive.

I tried two directive prototypes:

  1. Implement NgValueAccessor:
@Directive({
  selector: '[appNumberInput]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NumberInputDirective),
      multi: true
    }
  ]
})
export class NumberInputDirective implements ControlValueAccessor {
  constructor(private el: ElementRef<HTMLInputElement>) {}

  private onChange: (_: any) => void = () => {};

  private onTouched = () => {};

  writeValue(value: any): void {
    this.el.nativeElement.value = value != null ? value : '';
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  @HostListener('input', ['$event.target.value'])
  onInput(value: string): void {
    const parsedValue = parseFloat(value);
    this.onChange(isNaN(parsedValue) ? null : parsedValue);
  }

}
  1. Provide ngModel in the constructor:
@Directive({
  selector: '[appCustomNgModelInterceptor]',
  providers: [NgModel],
})
export class CustomNgModelInterceptorDirective {
  constructor(private ngModel: NgModel) {
    ngModel.valueChanges.pipe(tap((value) => {
      console.log('changed value', value); // this line is never triggered. 
    }));
  }
}

I would expect that the numberVal would be transformed to a string for input bindung but parsed back as float for the output binding.

None of these versions will update the bounded value.

Jupiter
  • 116
  • 7

0 Answers0