in Angular2 how to know when ANY form input field lost focus

In Angular2 how to know when ANY input field has lost focus..! If I use observables on the form:

form.valueChange.subscribe...

wont work since I really want to know when a field lost it's blur (focus) so I can update my store (if I update the store before losing focus, my cursor on a text input gets moved to the end, since the data gets swapped which is weird looking)

of course I can also add (change)="" on each input, but I have a lot of'em...

I was thinking something of the sorts of:

this.form.valueChanges.debounceTime(1000).subscribe((changes:any) => {
  if (this.form.dirty){
    this.appStore.dispatch(this.resellerAction.updateResellerInfo(changes))
  }
});

but the problem is that the dirty remains dirty, so it stuck in an everlasting loop of change detections...

tx

Sean


The blur event doesn't bubble, therefore we need to listen on every input element directly. Angular provides a nice solution for this situation.

A directive that applies to all input elements inside your template.

This directive uses a host-listener to listen for the blur events on all elements where the selector applies and forwards a bubbling input-blur event:

@Directive({
  selector: 'input,select',
  host: {'(blur)': 'onBlur($event)'}
})
class BlurForwarder {
  constructor(private elRef:ElementRef, private renderer:Renderer) {}

  onBlur($event) {
    this.renderer.invokeElementMethod(this.elRef.nativeElement, 
        'dispatchEvent', 
        [new CustomEvent('input-blur', { bubbles: true })]);
    // or just 
    // el.dispatchEvent(new CustomEvent('input-blur', { bubbles: true }));
    // if you don't care about webworker compatibility
  }
}

By adding the BlurForwarder directive to directives: [...] it will be applied to all elements in its template that match the selector.
The host-listener listens for bubbling input-blur events and calls our event handler:

@Component({
  selector: 'my-component',
  directives: [BlurForwarder],
  host: {'(input-blur)':'onInputBlur($event)'},
  template: `
<form>
  <input type="text" [(ngModel)]="xxx">
  <input type="text" [(ngModel)]="yyy">
  <input type="text" [(ngModel)]="zzz">
</form>`
}) {
  onInputBlur(event) {
    doSomething();
  }
}

Why not use focusout it bubbles by default in the DOM

Here's a simple directive that catches focusout and checks if input value is blank, then sets value to zero:

@Directive({
  selector: '[blankToZero]'
})
export class BlankToZeroDirective {
  constructor(private elementHost: ElementRef) { }

  @HostListener('focusout')
  ensureInput(): void {
    if (Util.isNullOrEmpty(this.elementHost.nativeElement.value)) {
      this.elementHost.nativeElement.value = 0;
    }
  }
}