Home > OS >  Custom pipe only update the input at second time
Custom pipe only update the input at second time

Time:08-19

Angular 13. I have an input control accept zip code like this:

<input type='text' [value]="addrFormGroup.get('Zipcode').value | formatZip" formControlName='Zipcode'> 

In the component class(code snipet):

@Input() public addrFormGroup: ormGroup;
@Input() addressObject: Address;

get Zipcode() { return this.addrFormGroup.get('Zipcode'); }

ngInit(){
  this.addressObject = new Address(this.AddressObject);

  if(this.addrFormGroup.pristine) {
    this.SetInitialalues();
    this.addrFormGroup.addControl("Zipcode", new FormControl({ value: this.AddressObject.Zipcode}));
  }
}

and the pipe:

@pipe({name 'formatZip', pure: false }) // pure = true, same result
export class formatZipPipe implements PipeTransform{
   transform(value: string): string{
      if(value == null) return value;

      let len = value.length;
      if(len != 0 && isNaN(number(value[len - 1]))){
          value = value.slice(0, len - 1);
      }
      return value;
   }
}

If I debug in VS code, and type 123a in the text field, set breakpoint at above last "return value;": the VARIABLES pane of VS Code shows input value is 123a and returning value is 123. The input field is however still 123a on the UI. If I type abcdef, after passing thru the pipe, the value on UI is ace. I have tried hours on it with no luck, any help appreciated!

CodePudding user response:

Explanation:

The way Angular works is different from React, that is:

1. By binding the [value] property of the input element with a fixed value, that value can be modified differently from React, this means that we can consider it as an initial value and not a forced value (like in React)

2. When binding the input element's [value] property with a variable value, this value will be the initial value that will be displayed in the UI on the first render of the component, but this time, when the component's variable is changed, this value will be reflected in the UI, example:

<div>
   <h1(click)="zip = zip   1;">
     Welcome to {{ title }}!
   </h1>
   <img
     width="300"
     alt="Angular Logo"
   </img>
   <form [formGroup]="form">
     <input type="text" [value]="zip" formControlName="zip" />
   </form>
</div>

Suppose the initial value of the zip variable is 1, if you change the input field (ie type something in it for example) to "abc", when clicking on the h1 element the input value would become 2

3. There are other use cases that I won't go into so as not to delve too deeply into the answer.

Solution(s):

1. You can use a directive to capture the (input) event and apply a change to the FormControl directive, either through the formControlName or formControl property binding, just use the NgControl abstraction that both derive from

Example:

Directive:
import { Directive, HostListener, Self } from "@angular/core";
import { NgControl } from "@angular/forms";

@Directive({
  selector: "[zip-format]"
})
export class ZipFormatDirective {
  constructor(@Self() private input: NgControl) {}

  @HostListener("input", ["$event"]) onInput($event) {
    const {
      target: { value }
    } = $event;

    const result = value.replace(/\D/g, "");

    this.input.control.setValue(result);
  }
}

Component HTML:

<div>
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img
    width="300"
    alt="Angular Logo"
  />
  <form [formGroup]="form">
    <input
      type="text"
      zip-format
      formControlName="zip"
    />
  </form>
</div>

Demo: https://codesandbox.io/s/morning-resonance-ny49ee?

2. You can use property binding [value] passing a component variable, adding a handler to the event (change) of the input element to make changes to the variable thus having a change in real time

Example:

<div>
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img
    width="300"
    alt="Angular Logo"
  />
  <form [formGroup]="form">
    <input
      type="text"
      [value]="zip"
      (input)="zip = removeNonNumbers($event);"
      formControlName="zip"
    />
  </form>
</div>

Limitation: As explained above, it will only set the value when there are changes, this means:

a. 123 => 123
Normal (no need for explanations)

b. 123d => 123
This is because when removing the non-numeric characters, I get the same value, that is, 123, and as 123 is not different from the previous value (123), then it lets it display in the UI as 123d

c. 123d4 => 1234
The value of the variable is as 123 (although the UI displays 123d), when typing another '4' key, the value of the variable became 1234, and 1234 is different from 123, so the value to be displayed on the UI is 1234

  • Related