0

I am creating a search page in Angular where the user can search for people via

  • the name
  • the date of birth

The search method takes the encapsulated search parameters as an argument:

    
inputName: Subject<string> = new Subject<string>();
inputDate: Subject<Date> = new Subject<Date>();

this.inputName.pipe(distinctUntilChanged(), debounceTime(300)).subscribe(
  input => {
    this.searchParameters.name = input;
    this.search();
  }
);

this.inputDate.pipe(distinctUntilChanged(), debounceTime(300)).subscribe(
  input => {
    this.searchParameters.bornBefore = input;
    this.search();
  }
);
<input (ngModelChange)="this.inputName.next($event)"
       [ngModel]="searchParameters.name"
       id="searchName"
       name="searchName"
       type="text">

<input (ngModelChange)="this.inputDate.next($event)"
       [ngModel]="searchParameters.bornBefore"
       class="form-control" id="searchDateOfBirth" name="searchDateOfBirth"
       type="date">

While this works, I would like to use just one Subject for the query object and debounce on that.

Simply declaring queryParamters: Subject<searchParameters> = new Subject<searchParameters>(); does not work, as changing on filter will clear the other one. I need to need to allow writing on the searchParamters object for both input events somehow and combine the changes.

wahok
  • 21
  • 5

2 Answers2

1
searchParamChanged($event, field:keyof searchParameters){
  if(this.searchParameters[field] === $event) return

  this.searchParameters[field] = $event;
  this.searchRequest.next(this.searchParameters);
}

private searchRequest = new Subject<searchParameters>()

// choose one of the two:

// 1) using angular async pipe to prevent subscription
searchResult$ = this.searchRequest.pipe(
  debounceTime(300),
  mergeMap(this.search$),
)

// 2) otherwise, for typical subscribe method, u need to unsubscribe
searchSubscriber = this.searchResult$.pipe(
  debounceTime(300),
  takeUntil(this.unsubscribe_notifier$),
)
.subscribe(this.search)

on the template (ngModelChange)="searchParamChanged($event, 'name')"

aii-yin
  • 39
  • 4
  • Where would I add distinctUntilChanged() there? As far as I understand this I have to apply this to each input independently? – wahok Mar 27 '23 at 13:45
  • 1
    q1: i replaced distinctUntilChanged using 'if checks' because i think it is easier and cleaner. You may include distinctUntilChanged in the pipe portion but that will require you to define a custom comparator functions since OBJECT cannot be compared by simply using `===` q2: yes, unfortunately. but that may be due to my inexperience of using ngModel. If you don't mind, I highly suggest you to use angular formGroup/formControl instead as it provides functionality that could simplify above code further. refer: https://angular.io/api/forms/AbstractControl#valueChanges – aii-yin Mar 27 '23 at 14:44
0

I would say there are several ways to make this code "prettier" 1st and most obvious one: use method, instead of .nexting subjects from template

updateSearch(overwrite: Partial<searchParameters>) {
  this.search$.next({
    ...this.searchParameters,
    overwrite,
  });
}
// template
(ngModelChange)="updateSearch({name: $event})"

or you could leave several subjects, but use one of combination operators to handle search in one subscription

combineLatest({ 
  name: 
    this.inputName.pipe(startWith(''), distinctUntilChanged()),
  bornBefore: 
    this.inputDate.pipe(startWith(new Date()), distinctUntilChanged()),
}).pipe(debounceTime(300))
.subscribe(
  params => {
    this.searchParameters = params;
    this.search();
  }
);

Andrei
  • 10,117
  • 13
  • 21