12

I got a formControl passed in @Input parameter that is bounded to input of number type that maximum value should be 10. When user types number that is bigger it should not change input value.

What is the way to either prevent event propagation or get old value and set it again?

I tried many other solutions from stack and github, but nothing solves my problem.

 valuecontrol: FormControl = new FormControl(0);

  constructor(){
    this.control.valueChanges.pipe(distinctUntilChanged()).subscribe(newValue=>{
      if(newValue >= 10){
        // set previous value
        const oldValue = this.control.value;
        console.log("old value = ", oldValue)
        this.control.patchValue(oldValue);
      }
    })
  }.

DEMO: https://stackblitz.com/edit/angular-6ocjfj?file=src/app/app.component.ts

tobias47n9e
  • 2,233
  • 3
  • 28
  • 54
karoluS
  • 2,980
  • 2
  • 23
  • 44
  • 1
    Why don't you just set max="10" on the input and set Validators.max(10) on the FormControl, to let the user know that what he typed is invalid? How would your technique work if the rule was that the value must be >=10? How would the user be able to type the 20 if typing 2 set back the value to blank because 2 is smaller than 10? That wouldn't work. Just use validation. – JB Nizet Nov 25 '18 at 23:58
  • The `valueChanges` event is fired after the new value is updated to the FormControl value that's why you are unable to get old value – Vikas Nov 26 '18 at 02:40

3 Answers3

29

After a year more of experience I think I found an optimal solution. To solve this problem the best way probably would be to use pairwise rxjs operator

Thank to that you are able to get the previous value of the stream.

The provided snippet does not solve the original problem as it would require few additional steps but It solves the original question on "How to get the old value?".

Here comes the code:

control: FormControl = new FormControl(0);

  constructor(){
    this.control.valueChanges.pipe(
      distinctUntilChanged(),
      pairwise() // gets a pair of old and new value
    ).subscribe(([oldValue, newValue])=>{
      console.log(oldValue, newValue)
      if(newValue >= 10){
        // set previous value
        this.control.patchValue(oldValue);
      }
    })
  }

Living code: https://stackblitz.com/edit/angular-tfrstg

karoluS
  • 2,980
  • 2
  • 23
  • 44
  • 2
    The problem I'm having with this is that if you change the value but don't emit an event, then the "previous" value emitted from the pairwise obs is no longer correct. I'm trying to listen for changes, check a condition, ask for confirmation, and revert if not confirmed. Problem is I need both old and new values to check the condition, and can't emit events after I revert. – Ross Brasseaux Jul 07 '20 at 01:35
  • @Lopsided In that case I would use a replay subject but that's a different case and I believe you should issue a separate question with your problem. – karoluS Jul 29 '20 at 19:12
  • 1
    Thanks @karoluS, I actually did that and did come up with a working solution, which I shared in my question. In case anyone is interested, here is the link: https://stackoverflow.com/q/62767003/1751792 – Ross Brasseaux Jul 29 '20 at 19:14
4

The valueChanges event is fired after the new value is updated to the FormControl value that's why you are unable to get the old value.

The best approach would be to use a validator as mentioned by @JB Nizet.

If you want to continue with your solution then you can leverage Angular's ngDoCheck life Cycle Hook to retain the old value.

Modified Code:

export class AppComponent implements DoCheck {
  private oldValue;
  control: FormControl = new FormControl(0);

  constructor() {
    this.control.valueChanges.pipe(distinctUntilChanged()).subscribe(newValue => {
      if (newValue >= 10) {
        // set previous value
        console.log("old value = ", this.oldValue)
        this.control.patchValue(this.oldValue);
      }
    })


  }
  ngDoCheck() {
    this.oldValue = this.control.value
  }
}


StackBlitz

tobias47n9e
  • 2,233
  • 3
  • 28
  • 54
Vikas
  • 11,859
  • 7
  • 45
  • 69
  • it is working, i will the answer open for now maybe somebody will come up with better solution. Thank you. – karoluS Nov 28 '18 at 09:46
0

Set a [max] attribute on the input control to 10:

<input type="number" [max]="10" [formControl]="control">

That way you can remove the newValue >= 10 condition entirely.

gotnull
  • 26,454
  • 22
  • 137
  • 203