2

I am creating a custom directive that suppose to reformat the text inside of the md-input. In my directive's ngOnInit and in @HostListener('blur', ['$event.target.value']) I have a logic to reformat the text that was entered by the user. And it is working except when the data is getting bound from the api call. I am wondering what event is getting fired when angular is updating that data so I can listen for it in my directive and fire my format logic for that data as well.

Update 1: added code to clear up things

        <input type="text"
               mdInput
               [(ngModel)]="item.Price"
               appMyPriceFormatter
               placeholder="Price"
               tabindex="5"
               [disabled]="disableInputs">

Directive code:

    import {Directive, ElementRef, HostListener, OnInit} from '@angular/core';
    import {CurrencyPipe} from '@angular/common';

    @Directive({
      selector: '[appMyPriceFormatter]'
    })
    export class MyPriceFormatterDirective implements OnInit {

      private el: HTMLInputElement;

      constructor(private elementRef: ElementRef,
                  private currencyPipe: CurrencyPipe) {
        this.el = this.elementRef.nativeElement;
      }

      ngOnInit() {
        if ((this.el.value !== null) && (this.el.value.trim() !== '')) {
          this.el.value = this.currencyPipe.transform(this.el.value, 'USD', true, '1.5-5');
        } else {
          this.el.value = null;
        }
      }

      @HostListener('focus', ['$event.target.value'])
      onFocus(value) {
        this.el.value = value.replace(/[^\d\-\.]/g, '');
      }

      @HostListener('blur', ['$event.target.value'])
      onBlur(value) {
        if ((value !== null) && (value.trim() !== '')) {
          this.el.value = this.currencyPipe.transform(value, 'USD', true, '1.5-5');
        } else {
          this.el.value = null;
        }
      }
    }
AlexanderM
  • 1,613
  • 4
  • 21
  • 35

3 Answers3

0

I think if you listen for ngModelChange event in your directive, you should be able to pick up the changes that happen to the input field asynchronously.

So inside your directive,

@HostListener('ngModelChange', ['$event'])
onInputFieldChange(value) {
  // logic for handling the change
}

See this for more on the ngModelChange event.

amal
  • 3,140
  • 1
  • 13
  • 20
  • This is not getting fired on initial data load, but getting fired for every other change which is not what I want – AlexanderM Oct 10 '17 at 18:53
  • can you show your code where you update the input element's value with the api call's data? – amal Oct 10 '17 at 18:56
0

I would simply change ngOnInit() to ngOnChange(), since the data arrives asynchronously.

Or you may not need the directive Ref: SO: Using Pipes within ngModel on INPUT Elements in Angular2-View (although I note the de-currencyfying upon focus).

<input type="text"
  mdInput
  [ngModel]="item.Price | currency"
  (ngModelChange)="item.Price=$event"
  placeholder="Price"
  tabindex="5"
  [disabled]="disableInputs">

Would be interesting to set up a fiddle for testing.

Here's a working Plunker of the concept,

@Pipe({ name: 'myPipe'})
export class MyPipe implements PipeTransform{
  transform(value, focused) {
    return (focused ? '' : '$') + 
      value.replace(/[^\d\-\.]/g, '')
  } 
}

@Component({
  selector: 'my-app',
  template: `<h1>Input with Currency Pipe</h1>
    <input type="text"
      [ngModel]="value | myPipe:focused"
      (ngModelChange)="value=$event"
      placeholder="Price"
      tabindex="5"
      [disabled]="disableInputs"
      (focus)="focused = true"
      (blur)="focused = false"
    >
  `
})
export class App { 
  value = '3.01';
  focused = false;
}

I have a feeling there must be a way to get rid of the focused variable as well.

Richard Matsen
  • 20,671
  • 3
  • 43
  • 77
0

Apparently I would be able to achieve that using a currency for ngModel and do something tricky in the handler for ngModelChange, however it would mean that on every page for every field I would need to have a separate function and so on. In addition to that is I used currency pipe just for the example sake in the reality I am doing something a bit trickier. With that in mind I do not want this logic spread to multiple functions and components so I ended up adjusting my directive further.

After digging a bit into lifecycle events it looks like ngDoCheck ( https://angular.io/guide/lifecycle-hooks ) will do the trick. The only part that I need to watch out is the recursion protection and protection against formatting it when user is typing so I end up with something like:

ngDoCheck() {
    if (this.focused) {
        return;
    }

    this.el.value = this.formatValue(this.el.value);
}
AlexanderM
  • 1,613
  • 4
  • 21
  • 35