DEV Community

Julian Dierkes
Julian Dierkes

Posted on • Updated on

Two Ways of Using Angular Services With the HttpClient

Angular Level:

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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();
  }
}
Enter fullscreen mode Exit fullscreen mode

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();
  }
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
codefinity profile image
Manav Misra

Many tuts only teach the pull approach, using Observable. I just started using BehaviorSubects 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.

Collapse
 
juliandierkes profile image
Julian Dierkes

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.