0
votes

I'm trying to implement a custom input that accepts only numeric values by reseting the rest to 0, using the following code for the input component:

import {Component, Input, Output, ElementRef, EventEmitter} from '@angular/core';
import {Observable} from 'rxjs/Rx';

@Component({
    selector: 'debounce-input',
    template: '<input type="text" [placeholder]="placeholder" [(ngModel)]="_v">'
})
export class DebounceInputComponent {
    @Input() placeholder: string
    @Input() delay: number = 300

    _v: string

    @Input()
    get v(): string {
        return this._v
    }

    set v(_value) {
        this._v = _value
        this.valueChange.emit(this.v)
    }

    @Output() valueChange: EventEmitter<any> = new EventEmitter<any>()

    @Output() value: EventEmitter<any> = new EventEmitter<any>()

    constructor(private elementRef: ElementRef) {
        const eventStream = Observable.fromEvent(elementRef.nativeElement, 'keyup')
            .map(() => this.v)
            .debounceTime(this.delay)
            .distinctUntilChanged()
        eventStream.subscribe((obj) => this.value.emit({v: this.v}))
    }
}

The component above is imported within AppComponent using the following code:

  1. HTML part:

    <div style="text-align:center">
      <debounce-input [v]="mynumber"
      delay="1000"
      placeholder="Type something..."
      (value)="handle($event)">
      </debounce-input>
    </div>
    
  2. TypeScript part:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'app';
    
      mynumber = 0
    
      handle(obj) {
        console.log(obj.v)
        if (isNaN(Number(obj.v))) {
          console.log('trying to reset invalid input to 0')
          this.mynumber = 0
        }
      }}
    

The problem is whenever I type a non-numeric value, the AppComponent fails to reset the input to 0, although the "trying to reset invalid input to 0" message gets shown within the console.

What is the cause of this behavior?

2
what is the value of console.log(obj.v) ? did you try with obj.target.value? - Niladri
@Niladri It's the same as any value I type in the input component. - user6039980
do you think you could set up a stackblitz for us to play with you code? - Boris Lobanov
@BorisLobanov I tried to do so, but stackblitz failed to detect the component I added. - user6039980
Whats the issue the question explanation is to confusing?? Do you want to restrict users for your input fields as type="number" or say type="text" - mayur

2 Answers

1
votes

I figured it out. You @Input value was not changed because mynumber in the parent was not changing from the child changes. I added this functionality and it worked:

handle(obj) {
    console.log(obj.v)
    if (isNaN(Number(obj.v))) {
      console.log('trying to reset invalid input to 0')
      this.mynumber = 0;
    } else {
      this.mynumber = obj.v; // add this line
    }
  }

P.S. I'd still, however, consider restricting your input to numeric type only and adding some filter to prevent setting numbers like '0123'.

0
votes

modify the setter in your input component, and make ngModel pointing to the v instead of _v.

  import {Component, Input, Output, ElementRef, EventEmitter} from '@angular/core';
  import {Observable} from 'rxjs/Rx';

  @Component({
      selector: 'debounce-input',
      template: '<input type="text" [placeholder]="placeholder" [(ngModel)]="v">'
  })
  export class DebounceInputComponent {
      @Input() placeholder: string
      @Input() delay: number = 300

      _v: string

      @Input()
      get v(): string {
          return this._v
      }

      set v(_value) {
        if (isNaN(Number(_value))) {
          console.log('trying to reset invalid input to 0')
          this._v = '0';
          return;
        }
          this._v = _value
          this.valueChange.emit(this.v)
      }

      @Output() valueChange: EventEmitter<any> = new EventEmitter<any>()

      @Output() value: EventEmitter<any> = new EventEmitter<any>()

      constructor(private elementRef: ElementRef) {
          const eventStream = Observable.fromEvent(elementRef.nativeElement, 'keyup')
              .map(() => this.v)
              .debounceTime(this.delay)
              .distinctUntilChanged()
          eventStream.subscribe((obj) => this.value.emit({v: this.v}))
      }
  }