Angular Js

How To Use Observables In Angular?

How To Use Observables In Angular?

How To Use Observable In Angular?

This Article helps you to Understand, How To Use Observable In Angular?


The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of state changes. This pattern is similar (but not identical) to the publish/subscribe design pattern.

Angular Observables facilitate sending messages between publishers (Observable Creators) and subscribers (Observable Users). Since observables are declarative, the function for publishing values is defined, but it is not used until a consumer subscribes.

Depending on the context, the observable may return numerous values of any type, including literals, messages, or events. As a publisher, you can create an Observable instance that defines a subscriber function. This function is executed when the consumer calls the subscribe() method.


Create Observables in Angular
To create an Observable in Angular, first it is necessary to import Observable from rxjs
import { interval, Subscription } from "rxjs";

To create a new Observable we just need to use its constructor and to add the logic that will be executed upon subscription.

public getData(){
    const data = new Observable(observer => {
      setTimeout(() => {
        observer.next(this.generateData());
        observer.complete();
      },3000)
    });
    return data
  }




Lets understand with a complete example

Create a service
In this observable example we are going to make the search function that return an observable which the AppComponent is going to subscribe. The return type is ObservableSearchItem[]>, and it will return an observable with an array of SearchItems as each item, with each item in the observable being a SearchItem.

By default, the http.get(...) function returns an Observable; more precisely, it returns an Observable of Response types, in which case the type would be ObservableResponse>. We just return that in its place.

Consequently, we must translate the raw JSON into our SearchItem model. We must essentially turn the Response into an array of SearchItems. We can accomplish that with our Observable by using a map operation to turn each Response into an array of SearchItems.



import { NgModule, Component, Injectable } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { HttpClientModule, HttpClient } from "@angular/common/http";
import { ReactiveFormsModule, FormControl, FormsModule } from "@angular/forms";

import {
  map,
  debounceTime,
  distinctUntilChanged,
  switchMap,
  tap
} from "rxjs/operators";

class SearchItem {
  constructor(
    public title: string,
    public category: string,
    public url: string
  ) {}
}

@Injectable()
export class SearchService {
  apiRoot: string = "https://merchandise.com/search";
  constructor(private http: HttpClient) {}

  search(filter: string): Observable<SearchItem[]> {

    let apiURL = `${this.apiRoot}?filter=${filter}&limit=20`;
    return this.http.get(apiURL)                                //#1
        .map(res => {                                           //#2
          return res.json().results.map(item => {               //#3
            return new SearchItem(                              //#4
                item.title,
                item.category,
                item.url
            );
          });
        });
   }
}

#1 this.http.get(apiURL) returns an Observable of response type.
#2 map is an observable operator which calls a function for each item on its input stream and pushes
the result of the function to its output stream. In this case each input item is a Response object.
#3 We loop over each item in the results property of the Response object and transform the item via a function.
#4 We convert the raw data returned from our API into an instance of SearchItem



Subscribing - One way to use above Observable in our Component would be just to subscribe to it and store the results locally,


class ItemsComponent {
  private loading: boolean = false;
  private results: SearchItem[];

  constructor(private items:SearchService) { }

  doSearch(filter:string) {
    this.loading = true;
    this.items.search(filter).subscribe( data => {
      this.loading = false;
      this.results = data #1
    });
  }
}

#1 We subscribe to the Observable<SearchItem[]>, data returned from the service and store each SearchItem[]
to our local results property.

<div  *ngFor="let item of results"> ... </div>



Using the async Pipe
Although the aforementioned is a nice beginning, we are not really utilising Observables to their full potential because, among other things, we are subscribing to the observable and storing the results locally on the component. We can avoid doing this by simply using the async pipe in our template.


class ItemsComponent {
  private loading: boolean = false;
  private results: Observable<SearchItem[]>;

  constructor(private items:SearchService) { }

  doSearch(filter:string) {
    this.loading = true;
    this.results = this.items.search(filter); #1
  }
}


#1 results now stores as Observable itself and not an array of SearchItems. We don’t need to
subscribe but simply store the result of calling items.search. (similar to iQueryable in .net)


To make the above work we need to use the async pipe in our template,
<div  *ngFor="let item of results | async"> ... </div>




Lets create another observable to create Auto-search Input box

Let's modify our programme so that it utilises a reactive form and searches as we write.

<form class="form-inline">
  <div class="form-group">
    <input type="search"
           class="form-control"
           placeholder="Enter search string"
           [formControl]="searchField"> #1
  </div>
</form>

#1 linked our searchField to our template form element with a [formControl] directive



import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

class AppComponent {
  private loading: boolean = false;
  private results: Observable<SearchItem[]>;
  private searchField: FormControl;

  constructor(private items:SearchService) { }

  ngOnInit() {
    this.searchField = new FormControl();
    this.searchField.valueChanges #1
      .debounceTime(400)
      .distinctUntilChanged()
      .subscribe(); #2
  }

  doSearch(term:string) {
    this.items.search(term)
  }
}

#1 subscribe to the observable our searchField exposes via the valuesChanged property,
we use debounceTime and distintUntilChanged so we only get notified when the user really wants us to make a query.
#2 Need to call subscribe to make it hot!


The valueChanges observable on our searchField is of type Observable<string>
Through a chain of operators we want to convert that Observable<string> into an Observable<SearchItem[]>.
So lets change our above code as below:-

ngOnInit() {
  this.searchField = new FormControl();
  this.searchField.valueChanges
    .debounceTime(400)
    .distinctUntilChanged()
    .map( term => this.items.search(term)) #3
    .subscribe( value => console.log(value));
}

#3 We call the search service for every emit of a search term on the input stream.



import {ReactiveFormsModule, FormControl, FormsModule} from '@angular/forms';

@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    FormsModule,
    HttpModule,
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  providers: [SearchService]
})



lets check our Logs

What’s happening is that items.search(term) isn’t returning SearchItem[] it’s returning Observable<SearchItem[]>.
So instead of converting a string to a SearchItem[], the map operator really converts a string to an ObservableSearchItem[]>.
Thus, instead of receiving SearchItem[] as we would like, the subscribe method is receiving ObservableSearchItem[]>.


How to get SearchItem[] then instead of ObservableSearchItem[]>?
Approach 1: using another subscriber

ngOnInit() {
  this.searchField = new FormControl();
  this.searchField.valueChanges
    .debounceTime(400)
    .distinctUntilChanged()
    .map( term => this.items.search(term))
    .subscribe( value => { #1
      value.subscribe( other => console.log(other) ) #2
    });
}

#1 First subscribe receives Observable<SearchItem[]>.
#2 Second subscribe again on each of these observables to get the SearchItem[].


Approach 2: use switch

switch expects a stream of Observables,
switch unsubscribes from any current Observables and subscribes to the new one when an Observable is pushed onto its input stream. It then emits any values from that Observable onto its output stream.

There is a combination operator called switchMap since combining switch with map is so popular.

ngOnInit() {
  this.searchField = new FormControl();
  this.results = this.searchField.valueChanges #1
    .debounceTime(400)
    .distinctUntilChanged()
    .switchMap( term => this.items.search(term));
}

#1 Our observable chain has now returned ObservableSearchItem[]>, so we can simply assign it to
our local results property.

Keep in mind that we are using the async pipe in our template. We also eliminated the subscribe
from our chain because async performs a subscription on our behalf.



Loading Indicator

The last step is to add the loading boolean to the chain and use the do operator to set it to true and false at the appropriate moments so that the loading message appears as intended.

The do operator allows us to do things other than merely manipulate objects via streams, so we utilise it in our programme to generate side effects. It should only be used sparingly, but in this case, setting the state of our component according to where we are in the processing chain, is a perfect illustration.


import 'rxjs/add/operator/do';

ngOnInit() {
  this.searchField = new FormControl();
  this.results = this.searchField.valueChanges
    .debounceTime(400)
    .distinctUntilChanged()
    .do( () => this.loading = true)
    .switchMap( term => this.items.search(term))
    .do( () => this.loading = false )
}






Related Post

About Us

Community of IT Professionals

A Complete IT knowledgebase for any kind of Software Language, Development, Programming, Coding, Designing, Networking, Hardware and Digital Marketing.

Instagram