Matthias Müller
Matthias Müller

Reputation: 3473

Angular2: Binding and Callbacks

I'm trying to create a small Directive to capture the windows global-keyup and then invoke a callback, so I basically captue the global window in a service and the keyup on my Directive:

export class EnterActivationDirective implements OnInit {
  private _enterClicked: Action;

  @Input() public set enterClicked(action: Action) {
    this._enterClicked = action;
  }

  constructor(public el: ElementRef, public windowWrapperService: WindowWrapperService) {
  }

  ngOnInit() {
    this.windowWrapperService.nativeWindow.onkeyup = this.onWindowKeyUp.bind(this);
  }

  private onWindowKeyUp(event: any) {
    if (event.code === 'Enter' && this._enterClicked) {
      this._enterClicked();
    }
  }
}

The Service and Action-Type aren't that interesting, since the Service just passes the native window and the Action-Type is a generic Callback without any parameters or return-value. The logic itself works, but I get some weird effects regarding the binding to the action. So, one of my other Components registers to the Directive:

<div appEnterActivation [enterClicked]="onKeyUp.bind(this)">
  <div>
... Amended

Which then triggers a search-operation:

 public search(): void {
    this.searchInProgress = true;
    const param = this.createSearchParams();
    this.searchStarted.emit(param);
    this.timeReportEntryApiService.searchTimeReportEntries(param)
      .then(f => {
        const newObjects = ArrayMapper.MapToNewObjects(f, new TimeReportEntry());
        this.searchFinished.emit(newObjects);
        this.searchInProgress = false;
      }).catch(f => {
        this.searchInProgress = false;
        throw f;
      });
  }

  public get canSearch(): boolean {
    return this.form.valid && !this.searchInProgress;
  }

  public onKeyUp(): void {
    debugger ;
    if (this.canSearch) {
      this.search();
    }
  }

Not too much logic here, but if the search is started from the callback, it seems like the properties and functions are in place, but they are on some kind of different object:

Since everything is working with a plain button, I'm almost certain it kindahow has to do with the callback and the binding to this.

I researched a bit regarding this bind, but regarding this thread Use of the JavaScript 'bind' method it seems to be needed. I also tested without binding, but then the this is bound to the global window variable.

Upvotes: 0

Views: 1125

Answers (2)

Chandermani
Chandermani

Reputation: 42669

Since you are using TypeScript you can use arrow function, that manages this correctly.

 public onKeyUp = () => {
    debugger ;
    if (this.canSearch) {
      this.search();
    }
  }

In that case you can just setup the property binding as

[enterClicked]="onKeyUp"

Upvotes: 1

Poul Kruijt
Poul Kruijt

Reputation: 71891

Why are you using an @Input? Angular made @Output for such a use case:

template:

<div appEnterActivation (enterClicked)="onEnter()"></div>

class:

export class EnterActivationDirective implements OnInit {

  @Output() 
  public readonly enterClicked: EventEmitter<any> = new EventEmitter();

  @HostBinding('document.keyup.enter')
  onEnter(): void {
      this.enterClicked.emit();
  }

}

No need for difficult checks or wrappers :)

Upvotes: 2

Related Questions