Ion-searchbar: pass filtered list results to another page

I´m trying to pass filtered list results to another page. I´m using the ion-searchbar in my filter-page.html:

<ion-searchbar [(ngModel)]="searchTerm" (ionChange)="setFilteredItems(searchTerm)"></ion-searchbar>

And here my typescript:

import { Component, OnInit } from '@angular/core';
import { searchService} from '../../services/search.service';
import { searchInter} from '../../interface/search';

@Component({
  selector: 'app-filter',
  templateUrl: './filter.page.html',
  styleUrls: ['./filter.page.scss'],
})
export class FilterPage implements OnInit {
  searchTitle$: SearchInter[] = [];
  public items: any;

  constructor(
      private searchService: SearchService) {
  }

  ngOnInit() {
  }
  
  setFilteredItem(searchTerm) {
    this.searchService.getTitlesForSearch(searchTerm).subscribe(item=> {
      this.searchTitle$ = item;
    });
  }
}

The request from the service works fine, I get my filtered items.
But how can I pass these filtered list to another page? Maybe I only want to to integrate my searchbar in a modal window and want press the results button to see these filtered results in the dashboard page.

Any idea?

My general answer to this is “via an Observable exposed in a mutually injected service”. SearchService would seem to be the logical choice here.

Thanks for your answer!

Could you please show me a short snippet, maybe how could be the observable in that context?

Hi,
You can create a subject behaviour in your service :

bar: BehaviorSubject<yourType> = new BehaviorSubject<yourType>(undefined);

undefined is default value you can change it.

when you need to pass data call .next(data)

this.service.bar.next(data);

then subscribe to get data:

this.service.bar.subscribe((data) => {});
1 Like

i have always been partial to emiting events with the results; here is an example from the angular documentation.

https://angular.io/guide/observables-in-angular#transmitting-data-between-components

1 Like

The primary reason I generally prefer doing this in a service is that I find it more extensible. When some other part of the app wants for some reason to know about these filtered list results (maybe we want to phone home with common search results for some sort of analytics; maybe we want to save the last X searches in device storage for performance or offline mode), all we have to do is inject the service and pay attention. If the EventEmitter is down in a component, it’s harder to get at from outside the host component. I guess that could be considered a feature in some cases, as it preserves some semblance of encapsulation, but I can think of a handful of times I’ve gone from an @Output property to a mutually injected service, and none the other direction. Of course, that may also just be me.

It really is an architectural choice, because I code in react and vue, emitting an event from a component is the the preferred method and recommended approach… now you could then have the parent component take the results of the modal and store it into some service or state manager but like I said it is a commonly used pattern in vue and react and the example I found in angular was from the angular documentation.

1 Like

Thanks for all your answers!

I don´t get a solution until now. Could you please take a look at my code an give me a note?

search.service.ts

...
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class searchService {
    api = '/items';

    private results = new BehaviorSubject([]);

    constructor(private http: HttpClient) {
    }

    // Results
    public getResults() {
        return this.results.asObservable();
    }
    public setFilteredItems(name: string) {
        this.http.get<any>(`${this.api}/list.json`).subscribe(response => this.results.next(response));
    }
}

filter.page.ts

setFilteredItems(searchTerm) {
    this.searchService.getTitlesForSearch(searchTerm).subscribe(items => {
        this.itemTitle$ = items;
    });
}

filter.page.html

<ion-card>
    <ion-searchbar [(ngModel)]="searchTerm" (ionChange)="setFilteredItems (searchTerm)"></ion-searchbar>
</ion-card>

<ion-button (click)="openResults()">
    Results: {{ itemTitle$.length }}
</ion-button>

resultslist.page.ts

...

import { SearchService } from '../../search.service';

...

export class ResultslistPage implements OnInit {
    public results = null;
    private resultList: any;

    constructor(
        private searchService: SearchService,
        private router: Router) {

        searchService.getResults().subscribe(this.resultList: any[] => {
            this.results = resultList;
        });
    }
}

The last part (searchService.getResults().subscribe …) is buggy, any idea how to fix it and get only a filtered result list in the resultslist.page.html ?

Never type the word any.

Obviously, that’s a slight exaggeration, but I do mean slight. Anywhere you can possibly give something a proper type, whether it be a controller property, a method argument, or a method return value, do so. It may seem tempting to just say “let’s just shut the compiler up for now” or “I’ll get around to it later”, but it even makes a huge difference when somebody else is reading your code for the first time, such as when you post questions here.

Naming conventions are also important, though less so. There are two problems with searchInter as an interface name: it needs to start with a capital letter, because classes and interfaces should be PascalCase - this way they don’t collide with variables and method names in camelCase. Also, Inter (or the Ixxx I often see from folks steeped in C#) isn’t useful. Since all the names of things I see here are so generic (search, item, result), I don’t know what you’re actually searching for. I’m going to pretend it’s books for now.

export class ResultsListPage {
  results: Book[] = [];
  constructor(private searchService: SearchService) {
    searchService.getResults().pipe(untilDestroyed(this))
      .subscribe(results => this.results = results);
  }
}

untilDestroyed comes from this project and is needed here to prevent leaking subscriptions. There are obviously other ways of doing this, such as keeping a Subscription property in the class or using the AsyncPipe, but untilDestroyed is my favorite.

Yes you´re right, the naming in my project are not good and I have to rename some interfaces and services.
Thanks for your help and I´ll try your code immediately!