0

We have been facing a problem of passing the event from the html to one of the Javascript methods that requires it.

export class SearchComponent implements OnInit {
    txtQueryChanged: Subject<string> = new Subject();

    constructor(private AService: AService, public _router: Router, location: PlatformLocation) {

        this.txtQueryChanged.debounceTime(1000)
        .distinctUntilChanged()
        .subscribe(model => {
             this.q = model;
             // Call function which calls API after a lag of 1 sec
             this.getDetails(model);
        });
    }

    watchChangesInSearchTerm(query: string, $event: 
        this.txtQueryChanged.next(query);
    }
    getDetails(event: any) {
        this.eventKey = event.which;
        if (this.q.trim() == "") {
            this.closeSearch();
        }
        // other programming logic including the API call
    }// end of function
}// end of class

now the HTML that calls this watchChangesInSearchTerm is as follows

<input type="text" class="searchfield" [(ngModel)]="q" name="searchfield"  (keyup)="watchChangesInSearchTerm(q, $event)" placeholder="What are you searching for today?">

Now the code from the HTML calls the watchChangesInSearchTerm method but it only passes the searchString int he parameter. The watchChangesInSearchTerm in turns debounces the model and calls the getDetails method. This method is also called for so many other use cases as well and thus requires the event through which it is triggered.

How can we pass the event to the getDetails method?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
koustubh
  • 96
  • 1
  • 7
  • Try to pass $event as the first argument, as in the documentation https://angular.io/guide/user-input – David Votrubec Mar 15 '18 at 11:15
  • Well, there are errors in the code, but I assume `this.getSearchDetails()` refers to `getDetails()`? In any case, you're receiving the event in `watchChangesInSearchTerm`, but you're not passing it to the call to `Subject.next()`, so of course you don't receive it in `getDetails`. You are passing whichever `q` points to in the template, which is not an `Event`. – Oscar Paz Mar 15 '18 at 11:26
  • Besides, you're setting `this.q` twice, first through `ngModel` and then through the event handler. This is not necessary and in fact can cause problems. – Oscar Paz Mar 15 '18 at 11:30
  • @OscarPaz , you are right. Exactly thats what I wanted to know. How to pass it on to Subject.next() ? – koustubh Mar 15 '18 at 11:33

2 Answers2

0

Well, my suggestion:

txtQueryChanged: Subject<KeyboardEvent> = new Subject();
constructor(private AService: AService, public _router: Router, location: PlatformLocation) {

    this.txtQueryChanged.debounceTime(1000)
    .distinctUntilChanged()
    .subscribe(event => {
         // this.q = model; --> Not necessary! ngModel already updates q
         // Call function which calls API after a lag of 1 sec
         this.getDetails(event);
    });
}

watchChangesInSearchTerm($event: KeyboardEvent)
    this.txtQueryChanged.next($event);
}

Then, in your template, just do, as you don't need the searchTerm here:

<input type="text" class="searchfield" [(ngModel)]="q" name="searchfield"  (keyup)="watchChangesInSearchTerm($event)" placeholder="What are you searching for today?">

You are using ngModel with two-way binding, so you don't need to manually update q with data from the template, that's what ngModel is for. You just need to listen for key events, and react consequently.

In any case, if you really needed to pass both model and event to Subject.next(), you can easily:

interface EventModel {
    model: string;
    event: KeyboardEvent;
}

(...)

txtQueryChanged: Subject<EventModel> = new Subject()

(...)

this.txtQueryChanged.next({
    model: model,
    event: event
});

The definition of an interface is not necessary, you could do with defining the Subject as Subject<any> but, using TypeScript, I always like to annotate my types. It might be cumbersome at first but it pays off in the long term.

Besides this, I'd suggest you to use 'keypress' instead of 'keyup', but that's up to you and your needs.

Oscar Paz
  • 18,084
  • 3
  • 27
  • 42
0

In my application i'm doing a search like this in the html

<input type="text" name="search" #search class="form-control" (keyup)="doSearch(search.value)" required>

In de Component.ts

export class SearchViewComponent implements OnInit {
  @Output('searchQuery') sq: EventEmitter<string> = new EventEmitter();

  search: string;
  searchChanged: Subject<string> = new Subject<string>();

  constructor() {
    this.searchChanged
      .debounceTime(300)
      .distinctUntilChanged()
      .subscribe(searchValue => this.search = searchValue);
  }

  ngOnInit() {
  }

  doSearch(searchText) {
    this.sq.emit(searchText);
    this.searchChanged.next(searchText);
  }

}

I also emit to the parent component @Output