Angular Level:
The Old Fashioned Way: The 'Pull' Approach
When starting to build an Angular app you most likely come to the point where you need some kind of HTTP communication.
I guess every intermediate Angular user has used the http client before.
Most likely it was wrapped in an Angular service.
E.g. a service for a shopping list app could look like this:
@Injectable({
providedIn: 'root'
})
export class ShoppingListPullService {
ITEMS_URL = 'assets/items.json';
constructor(private httpClient: HttpClient) {
}
getItems(): Observable<ShoppingItem[]> {
return this.httpClient.get<ShoppingItem[]>(this.ITEMS_URL);
}
}
A component then gets the shopping items via a simple method call, stores them and can display them like this:
@Component({
selector: 'app-pull',
template: `
<div *ngFor="let user of items">
- {{user.quantity}} {{user.name}}
</div>
`
})
export class PullComponent implements OnInit {
items: ShoppingItem[];
constructor(private readonly shoppingListService: ShoppingListPullService) {
}
ngOnInit(): void {
this.shoppingListService.getItems()
.subscribe(items => this.items = items);
}
}
Note that we don't need to unsubscribe
here, as the angular HttpClient
handles this for us.
If we had another kind of Observable here, we would also need to unsubscribe
.
Let's call this approach the "Pull Approach", because it pulls the data from the service and the component is responsible for holding the state (in this case the array of shopping items).
After any possible change of the items a refetch must be done including the corresponding subscribe block.
This approach is nice and simple, but may not be suitable if multiple components use the service and all of them should share one consistent state.
E.g. imagine you have a component for listing the items and one component for adding new items.
Both components need control of the list.
Observable Data Service: The 'Push' Approach
Here an "Observable Data Service" or "Service With a Subject" is much more convenient.
It may look like this:
@Injectable({
providedIn: 'root'
})
export class ShoppingListPushService {
ITEMS_URL = '/assets/items.json';
private readonly items$: BehaviorSubject<ShoppingItem[]> = new BehaviorSubject<ShoppingItem[]>([]);
constructor(private httpClient: HttpClient) {
}
fetchList() {
this.httpClient.get<ShoppingItem[]>(this.ITEMS_URL)
.subscribe(receivedItems => this.items$.next(receivedItems));
}
get items(): Observable<ShoppingItem[]> {
return this.items$.asObservable();
}
}
Let's have a look on how this works:
The fetchList
method executes an HTTP request.
After it returned the items successfully they are published to the BehaviourSubject items$
, which means that anyone subscribed to this subject will get that new array of items.
This Subject is made public in the form of an Observable by the get
method below so nothing can be published from outside of the service.
This kind of service can easily be used with the Angular async
pipe.
No need to subscribe
to or unsubscribe
from anything.
@Component({
selector: 'app-push',
template: `
<div *ngFor="let item of shoppingListService.items | async">
- {{item.quantity}} {{item.name}}
</div>
`
})
export class PushComponent implements OnInit {
constructor(readonly shoppingListService: ShoppingListPushService) {
}
ngOnInit(): void {
this.shoppingListService.fetchList();
}
}
Only thing left to do programmatically in the list component is to trigger the data fetch.
So let's again imagine a second component using this service to add items to the shopping list.
After adding an item the component may easily trigger the fetchList()
method and cause an update of the item list in the other component.
Pretty easy, huh?
Top comments (2)
Many tuts only teach the pull approach, using
Observable
. I just started usingBehaviorSubect
s and am wondering why it's not mentioned more.I'm glad to see some reassurance here that I am not just doing things in a 'weird' way.
I'm wondering if there are any practical use cases where
Observable
and pull approach is better.I guess the "pull" approch is easier to understand and is therefore teached first, but the more experience you gain the more upsides of the "push" approch you will find.