0

I have my search implemented in Angular using async pipe with an observable. I assign the observable to the search observable done with switchmap. But it does not work! I think it has something to do with one assigning is overwriting the other one. But I need to have some data show before beginning to search (who doesnt anyway.) I tried to reverse it by assigning the data first and then the search but then no data is shown and it only works when I begin typing.

So my question is how can I fix it so I can have the search feature and still display some data at the beginning. Using async pipe and not subscribing in component

And the scenario to make it clearer > At component initialization I want to make a request to server and get initial data display it. And also 'assigning' my same observable to the input where the user types criteria to search, by making another request to the server.

Any help is appreciated

Similar question but no concrete answer and deprecated rxjs operators

Angular2 - get data from service on init with Switchmap

queryField: FormControl;
customerOrdersObservable: Observable<NextCustomerOrdersPaginationModel>;

ngOnInit() {
    this.customerOrdersObservable = this.queryField.valueChanges.pipe(
      debounceTime(500),
      distinctUntilChanged(),
      switchMap((searchInput) => {
        this.transformSearchInput(searchInput);
        return this.customerOrderService.getSearchedCustomerOrders(this.page, this.pageSize, this.searchTerm);
      }));
    this.customerOrdersObservable = this.customerOrderService.getSearchedCustomerOrders(
      this.page, this.pageSize, this.searchTerm);
  }

Edit to

ngOnInit() {
    this.customerOrdersObservable = this.queryField.valueChanges.pipe(
      startWith(this.customerOrderService.getSearchedCustomerOrders(
      this.page, this.pageSize, this.searchTerm)), > 'would have been great but its not possible (unless im wrong)'
      debounceTime(500),
      distinctUntilChanged(),
      switchMap((searchInput) => {
        this.transformSearchInput(searchInput);
        return this.customerOrderService.getSearchedCustomerOrders(this.page, this.pageSize, this.searchTerm);
      }));
  }
Merv
  • 1,039
  • 2
  • 12
  • 22

1 Answers1

0

The issue is that you overwrite the observable right after it's been assigned the first time. It sounds like you want the observable to immediately emit an initial value, then after each value change (eg, you type something) it should load the other data.

This is what startWith is for.

ngOnInit() {
  const initialData = {};
  this.customerOrdersObservable = this.queryField.valueChanges.pipe(
    debounceTime(500),
    distinctUntilChanged(),
    switchMap((searchInput) => {
      this.transformSearchInput(searchInput);
      return this.customerOrderService.getSearchedCustomerOrders(this.page, this.pageSize, this.searchTerm);
    }),
    startWith(initialData),
  );
}

// After a bit of clarification: OP wants the initial data to be fetched from the backend initially. There's a couple of working solutions, I'll try to stick with startWith.

Since your observable will only emit values after queryField.valueChanges emits a new value, we need to define a starting point for it. We can use startWith again and just "mock" its initial emitting value:

ngOnInit() {
  const initialQueryFieldData = {}; // You will have to define this depending on the type `queryField.valueChanges` emits.
  this.customerOrdersObservable = this.queryField.valueChanges.pipe(
    startWith(initialQueryFieldData),
    debounceTime(500),
    distinctUntilChanged(),
    switchMap((searchInput) => {
      // The very first emit should have `initialQueryFieldData` as `searchInput`
      this.transformSearchInput(searchInput);
      return this.customerOrderService.getSearchedCustomerOrders(this.page, this.pageSize, this.searchTerm);
    }),
  );
}
Philipp Meissner
  • 5,273
  • 5
  • 34
  • 59
  • I looked up startwith. But it does not help me in this case since my initial data is coming from a request made to the server. The last 2 lines from my ngoninit method. – Merv Apr 29 '20 at 06:04
  • In that case just move the `startWith` to the beginning, I will update my answer. – Philipp Meissner Apr 29 '20 at 06:09
  • ok I understand that I need a starting point because of the observable valuechanges BUT startWith does not accept an Observable (unless Im wrong) it only accept the type of which queryfield emits which I don't have because response (data) is coming from the server. I could wrap it inside the subscribe method of the initial observable with init data and then assign the initial data but I don't want to use subscribe in component. Only async in template. I will update my question. – Merv Apr 29 '20 at 06:21
  • In the second example you should add the initial fields that you want `searchInput` to contain, as it should immediately emit a value (`searchInput`) no matter if `queryField.valueChanges` did in fact emit a value or not (which it initially does not, as you said). – Philipp Meissner Apr 29 '20 at 06:24
  • Doesn't work man. I tried initializing with a new empty object of the type which queryField Valuechanges emits but when I do that it just displays nothing until I start typing. I need at component init to display the data coming from the server (dynamic data) and also have the search input working when searching. With my original configuration I only get one of them. Either search working or init data but not both. And the working of either one is depending which I assign last. If you have any other way do let me know. – Merv Apr 29 '20 at 06:43
  • I will reply later more in-depth. But for the moment you could just assign the result in an `subscribe` block, then you can initially just do the search and for the other values you have the piped-construct that you built earlier. – Philipp Meissner Apr 29 '20 at 06:45
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/212757/discussion-between-merv-and-philipp-meissner). – Merv Apr 29 '20 at 06:46