I am developing a simple search screen to learn angular subscription which i find to be very confusing. In my home page page I created two components. One filter components and second search result component.
Here is my filter service
export class FilterService {
private filtersubject = new Subject<Filter>();
public currentfilters: Observable<Filter>;
constructor() {
this.filtersubject = new BehaviorSubject<Filter>(Filter.getInstance());
this.currentfilters = this.filtersubject.asObservable();
}
getFilters(): Observable<Filter> {
return this.filtersubject.asObservable();
}
sendFilterMessage() {
this.filtersubject.next(Filter.getInstance());
}
clearFilterMesasage() {
this.filtersubject.next(new Filter());
}
}
Here is my search result service
export class SearchresultsService {
public searchresult!: Observable<ResultData[]>;
private searchresultsubject = new Subject<ResultData[]>();
private f: Filter = Filter.getInstance();
subscription: Subscription;
constructor( private httpClient: HttpClient, private filterservice: FilterService ) {
this.subscription = this.filterservice.currentfilters.subscribe(
(filter: Filter) => {
this.f = filter;
this.searchData();
this.searchresult = this.searchresultsubject.asObservable();
});
}
public searchData(): void {
const url = `${environment.ssoUrl}api/SearchrResult`;
this.httpClient.get<ResultData[]>(url, { params, responseType : 'json'}).
subscribe((response: ResultData[]) => {
this.searchresultsubject.next(response);
});
}
}
in search result i have a link for each record that navigates to details page. There you do some CRUD operation and on Submit navigate back to home page. The problem is that after CRUD operation and navigation back to search screen the subscription never got cleared and retains the previous search options. In search components i have
resetFilterData(formid: number){
this.filterservice.clearFilterMesasage();
this.router.navigate(["details", id]);
}
Also in homecomponents.ts I have
export class HomeComponent implements OnInit, OnDestroy {
subscription: any;
constructor(
private router: Router,
public filterSer: FilterService ) {
this.subscription = this.filterSer.currentfilters.subscribe();
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
ngOnInit() {
//some code
}
}
Am I missing something in the code or not doing it right. why did the subscription not reset after navigation. Please advice THanks
As the first comment suggests, this does seem overly complicated. I hope this example may provide a simpler setup in creating a search list with applied filters.
First, let's simplify your search result service. I typically follow the strategy that a service should be as generic as possible. So, all we want to do is request and return the complete list of results.
export class SearchresultsService {
public searchresult$: Observable<ResultData[]>;
constructor(private httpClient: HttpClient) {
const url = `${environment.ssoUrl}api/SearchrResult`;
this.searchresult$ = this.httpClient.get<ResultData[]>(url, { params, responseType: 'json' });
}
}
Service files are where you can define observables, but you should try to avoid subscriptions. Leave that to the components that depend on the service's data.
Next, I created a sample list component that manages filters as local state. I took some liberties in assuming how your filter object actually works.
export class ListComponent {
public searchResults$: Observable<ResultData[]>;
private filter = new BehaviorSubject<Filter>(new Filter());
constructor(private searchService: SearchresultsService) {
this.searchResults$ = this.searchService.searchResult$.pipe(
switchMap(results => this.filter.pipe(
map(filter => results.filter(result => result[a] === filter[a]))
))
);
}
public setFilter(criteria: any) {
const newFilter = new Filter(criteria);
this.filter.next(newFilter);
}
}
Let's go through this step by step:
setFilter()
that lets you create and emit a new filter from the same BehaviorSubject.switchMap()
to subscribe to our filter BehaviorSubject to get the latest filtered value and apply it.From here, you can subscribe to the searchResult$
observable in your component's template using the async
pipe. This will automatically handle the subscribe and unsubscribe events throughout the component's lifecycle.
<ul class="search-results">
<li *ngFor="let result of searchResult$ | async">
<!-- do things with {{result}} -->
</li>
<ul>
Now, when you click through the CRUD page, the list component will be destroyed along with all subscriptions. When you navigate back, the filter
BehaviorSubject will be re-initialized with the default blank filter.