Reputation: 11599
The following are two approaches to the same problem, is to do a reactive search for some characters that user enter in a text box. The first solution is from ngrx example and the second one is from an egghead instant search course.
Observable
while the second uses Subject
.Observable
solution uses takeUntil
to call the server only once. The Subject
solution uses distinctUntilChanged
.Can someone explain the advantages and disadvantages with these two approaches?
Search using Observable:
@Injectable()
export class BookEffects {
@Effect()
search$: Observable<Action> = this.actions$
.ofType(book.ActionTypes.SEARCH)
.debounceTime(300)
.map(toPayload)
.switchMap(query => {
if (query === '') {
return empty();
}
const nextSearch$ = this.actions$.ofType(book.ActionTypes.SEARCH).skip(1);
return this.googleBooks.searchBooks(query)
.takeUntil(nextSearch$)
.map(books => new book.SearchCompleteAction(books))
.catch(() => of(new book.SearchCompleteAction([])));
});
constructor(private actions$: Actions, private googleBooks: GoogleBooksService) { }
}
@Component({
selector: 'bc-find-book-page',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<bc-book-search [query]="searchQuery$ | async" [searching]="loading$ | async" (search)="search($event)"></bc-book-search>
<bc-book-preview-list [books]="books$ | async"></bc-book-preview-list>
`
})
export class FindBookPageComponent {
searchQuery$: Observable<string>;
books$: Observable<Book[]>;
loading$: Observable<boolean>;
constructor(private store: Store<fromRoot.State>) {
this.searchQuery$ = store.select(fromRoot.getSearchQuery).take(1);
this.books$ = store.select(fromRoot.getSearchResults);
this.loading$ = store.select(fromRoot.getSearchLoading);
}
search(query: string) {
this.store.dispatch(new book.SearchAction(query));
}
}
Search using Subject:
import { Component } from '@angular/core';
import { WikipediaSearchService } from './wikipedia-search.service';
import { Subject } from 'rxjs/Subject';
//application wide shared Rx operators
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/switchMap';
@Component({
moduleId: module.id,
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css']
})
export class AppComponent {
items:Array<string>;
term$ = new Subject<string>();
constructor(private service:WikipediaSearchService) {
this.term$
.debounceTime(400)
.distinctUntilChanged()
.switchMap(term => this.service.search(term))
.subscribe(results => this.items = results);
}
}
<div>
<h2>Wikipedia Search</h2>
<input (input)="term$.next($event.target.value)">
<ul>
<li *ngFor="let item of items">{{item}}</li>
</ul>
</div>
Upvotes: 1
Views: 1587
Reputation: 2905
Behavior Subject is a type of subject, a subject is a special type of observable so you can subscribe to messages like any other observable. The unique features of a behavior subject are:
It is a observer in addition to being a observable so you can also send values to a subject in addition to subscribing to it. In addition you can get a observable from behavior subject using the asobservable() method on behaviour subject.
Observable is a Generic, and Behavior subject is technically a sub-type of Observable because behavior subject is a observable with specific qualities.
An observable can be created from both Regular and Behavior Subjects using subject.asobservable(). Only difference being you can't send values to a observable using next() method.
In angular2 services, I would use behavior subject for a data service as a angular service often initializes before component and behavior subject ensures that the component consuming the service receives the last updated data even if there are no new updates since the component's subscription to this data. In short many things are similar in observable/subject/BehaviorSubject etc it depends on your need.
Upvotes: 2