1

I want to have simple number input component that allows only for numbers from specific range.

I use HTML min/max attributes that works when user is using input "arrows"

<input type="number" style="width: 100px" [(ngModel)]="value" min="0" max="99" />

And I am checking also the values when propagating it further to application:

export class AppInput {
  _v: number;

  @Input()
  get value(): number {
    return this._v;
  }
  set value(v) {
    if (v > 99) {
      v = 99;
    }
    this._v = v;
    this.valueChange.emit(v);
  }
  @Output() valueChange = new EventEmitter<number>();
}

When user write directly to the input, when he e.g. press 0 when value is 10 - input is correctly changed to 99. However when he continues, content of input is changed to 990 (even though model is correctly 99).

How can I limit user what he can enter into input component?

Sample project: https://codesandbox.io/s/w23900kx05

2
  • You can also use a custom pipe to achieve that. Commented Oct 1, 2018 at 8:08
  • The sample project seems working for me. So I can't reproduce Commented Oct 1, 2018 at 9:07

3 Answers 3

2

I don't where is the error but this should work.

   <input type="number" style="width: 100px" [(ngModel)]="value" oninput="this.value = this.value > 99?99:this.value" min="0" max="99" />

If 99 is not fixed

<input type="number" style="width: 100px" 
  [(ngModel)]="value" 
 (keypress)='inputChanged()'
  min="0" max="99" />

in your .ts file

 export class AppInput {
      maxNumber = 50 // u want to only change this;
      maxNumberBackup = this.maxNumber;

      _v: number;

    constructor(){
      this.maxNumber = this.maxNumber -1;
    }
      inputChanged() {
        return this.value > this.maxNumber ? false : true;
      }

      @Input()
      get value(): number {
        return this._v;
      }
      set value(v) {
        if (v > this.maxNumberBackup) {
          v = this.maxNumberBackup;
        }
        this._v = v;
        this.valueChange.emit(v);
      }
      @Output() valueChange = new EventEmitter<number>();
    }
Sign up to request clarification or add additional context in comments.

7 Comments

do I understand it right that we are bypassing angular here? could I access also angular component somehow - e.g. value 99 is not fixed?
you can replace 99 with a variable.
This is a nice workaround, but it would be interesting to know why it does happen
yeah. it can be a bug. :)
thanks, you both with @Ashish provide me working solutions and I can't accept both. I consider his solution more "angular" way, your is more JS oriented. Thus I will upvote your answer but mark his answer as accepted.
|
0

I don't understand why do you need an Input property value for your custom Input from the parent. (Probably you want some default value?).

Coming to the solution of your question. Why is your view(input box) not updating even if the model has the correct maximum value? Let's see it this way:

You have a property value which is bound to the input box. So when the input value changes, this is how value's data will be updated. Consider that initially value = 0, then you input 1 in the input box, due to 2-way binding, value will be updated with one. Will it be marked for change detection here? Yes! And this will again update the Input Box on Change Detection cycle. Now, you input 2(input box now contains 12). Again It will be marked for Change Detection. Now, you input 3(The input should had contained 123) but because of your logic, it is changed to 99. So what changes actually? The data in the value property. Now your input box contains 99. You enter another 0. What happens? Your setter logic will make value to 99.(But wait it was already 99). So Is is it marked for Change detection? No. So will the Input which is bound to value see any difference? No.

Note: At least one change detection cycle should run on all your input events in the input box.

How I solved your problem is (I have used property data instead of value):

constructor(private _cdr: ChangeDetectorRef) {}

@Input() set data(v) {

  this._v = v; // I am assigning whatever value I get to the data property
               // so that in the manual change detection a change can be seen
               // (even though that change doesn't need to be show) 
  this._cdr.detectChanges();
  if (v > 99) {
    v = 99
  }
  else if (v < 0) {
    v = 0
  }
  this._v = v
  this.valueChange.emit(v);
}

In the very first step, I am assigning whatever value I get from the input box to _v, then I call a manual change detection before proceeding with my logic. So the value of data is updated with the Input Value. My logic will revert it back to 99 or 0, it will be marked for change detection now, and your view will be updated apprpriately (Note: Your output will emit the final value itself.)

See the working example here: https://stackblitz.com/edit/angular-custom-number-input?file=src/app/hello.component.ts

Comments

0

If you are looking to validate length use maxLength instead should work.

  <input type="text" style="width: 100px" [(ngModel)]="value" maxLength="2" />

1 Comment

value 99 is just example - could be 50 or anything else

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.