I am here to continue the discussion around AngularJS 2.0. In my previous article, I have already discussed about the http module including get and post method calling in Angular 2.0. Now, in this article, we will discuss about the observables concept in Angular 2.0. In case, you did not have a look at the previous articles of this series, go through the links mentioned below.
We already discussed that in Angular 2.0, there are many new features introduced in Angular 2.0. An exciting new feature used with Angular is Observable. This isn't an Angular specific feature but rather a proposed standard for managing async data that will be included in the release of ES7. Observables open up a continuous channel of communication in which multiple values of data can be emitted over time. From this, we get a pattern of dealing with data by using array-like operations to parse, modify and maintain the data. Angular uses Observables extensively - you'll see them in the HTTP service and the event system.
In version 2.0, Angular mainly introduces the reactive programming concept based on the observables for dealing the asynchronous processing of data. In Angular 1.x, we basically used promises to handle the asynchronous processing. But still in Angular 2.0, we can still use the promises for the same purpose. The main concept of reactive programing is the observable element which is related to the entity that can be observed. Basically, at normal look, promises and observables seem very similar to each other. Both of them allow us to execute asynchronous processing, register callbacks for both successful and error responses, and also notify or inform us when the result is there.
How to define Observables
Before going into detailed example of observables, first we need to understand how to define an observable object. To create an observable, we can use the create method of the Observable object. A function must be provided as parameter with the code to initialize the observable processing. A function can be returned by this function to cancel the observable.
- var observable = Observable.create((observer) =>
- {
- setTimeout(() => {
- observer.next('some event');
- }, 500);
- });
Similar to promises, observables can produce several notifications using different methods from the observer.
- next
Emit an event. This can be called several times.
- error
Throw an error. This can be called once and will break the stream. This means that the error callback will be immediately called and no more events or completion can be received.
- complete
Mark the observable as completed. After this, no more events or errors will be handled and provided to corresponding callbacks.
Observables allow us to register callbacks for previously described notifications. The subscribe method tackles this issue. It accepts three callbacks as parameters,
- The onNext callback that will be called when an event is triggered.
- The onError callback that will be called when an error is thrown.
- The onCompleted callback that will be called when the observable completes.
Observable specificities
Observables and promises have many similarities, and inspite of that, observables provide us some new specifications like below -
- Lazy
An observable is only enabled when a first observer subscribes. This is a significant difference compared to promises. Due to this, processing is provided to initialize a promise is always executed even if no listener is registered. This means that promises don’t wait for subscribers to be ready to receive and handle the response. When creating the promise, the initialization processing is always immediately called. Observables are lazy so we have to subscribe a callback to let them execute their initialization callback.
- Execute Several Times
Another particularity of observables is that they can be triggerred several times unlike promises which can’t be used after they were resolved or rejected.
- Canceling Observables
Another characteristic of observables is that they can be cancelled. For this, we can simply return a function within the initialization call of the Observable.create function. We can refactor our initial code to make it possible to cancel the timeout function.
Error Handling
If something unexpected arises, we can raise an error on the Observable stream and use the function reserved for handling errors in our subscribe routine to see what happened.
- import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
- import { Observable } from 'rxjs/Observable';
-
- @Component({
- moduleId: module.id,
- selector: 'home-page',
- templateUrl: 'app.component.homepage.html'
- })
-
- export class HomePageComponent implements OnInit {
-
- private data: Observable<Array<number>>;
- private values: Array<number> = [];
- private anyErrors: boolean;
- private finished: boolean;
- private status: string;
-
- constructor() {
- }
-
- ngOnInit(): void {
-
- }
-
- private onClick(): void {
- let self = this;
- this.data = Observable.create(observer => {
- setTimeout(() => {
- observer.next(10);
- }, 500);
-
- setTimeout(() => {
- observer.next(100);
- }, 1000);
-
- setTimeout(() => {
- observer.complete();
- }, 2000);
- this.status = "Started";
- });
-
- let subscription = this.data.subscribe(
- (value) => {
- let data: any = { val: value };
- self.values.push(data);
- },
- error => self.anyErrors = true,
- () => self.finished = true
- );
- }
-
- private onClick1(): void {
- let self = this;
- this.data = Observable.create(observer => {
- setTimeout(() => {
- observer.next(10);
- }, 500);
-
- setTimeout(() => {
- observer.next(100);
- }, 1000);
-
- setTimeout(() => {
- observer.complete();
- }, 2000);
- this.status = "Started";
- });
-
- let subscription = this.data.forEach(
- (value) => {
- let data: any = { val: value };
- self.values.push(data);
- })
- .then(() => self.status = "Ended");
- }
- }
app.component.homepage.html
- <div>
- <h3>Observables Sample</h3>
- <b>Angular 2 Sample Component Using Observables!</b>
-
- <h6 style="margin-bottom: 0">Count:</h6>
- <div *ngFor="let item of values">{{ item.val }}</div>
-
- <h6 style="margin-bottom: 0">ERRORs:</h6>
- <div>Errors: {{anyErrors}}</div>
-
- <h6 style="margin-bottom: 0">FINISHED:</h6>
- <div>Finished: {{ finished }}</div>
-
- <h6 style="margin-bottom: 0">STATUS:</h6>
- <div>{{status}}</div>
-
- <button style="margin-top: 2rem;" (click)="onClick()">Init Page</button>
- <error-page></error-page>
- </div>
app.component.error.ts
- import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
- import { Observable } from 'rxjs/Observable';
-
- @Component({
- moduleId: module.id,
- selector: 'error-page',
- templateUrl: 'app.component.error.html'
- })
-
- export class ErrorComponent implements OnInit {
-
- private data: Observable<Array<number>>;
- private values: Array<number> = [];
- private anyErrors: any;
-
- constructor() {
- }
-
- ngOnInit(): void {
-
- }
-
- private onClick(): void {
- let self = this;
- this.data = Observable.create(observer => {
- setTimeout(() => {
- observer.next(10)
- }, 1500);
- setTimeout(() => {
- observer.error('Hey something bad happened I guess');
- }, 2000);
- setTimeout(() => {
- observer.next(50)
- }, 2500);
- });
-
- let subscription = this.data.subscribe(
- (value) => {
- let data: any = { val: value };
- self.values.push(data);
- },
- error => self.anyErrors = "Ended"
- );
- }
- }
app.component.error.html
- <div>
- <h3>Observables Sample - Error handling</h3>
- <b>Angular 2 Component using Observables!</b>
-
- <h5 style="margin-bottom: 0">VALUES</h5>
- <div *ngFor="let item of values">{{ item.val.toString() }}</div>
-
- <h5 style="margin-bottom: 0">ERRORS</h5>
- <pre><code>{{anyErrors}}</code></pre>
-
- <button style="margin-top: 2rem;" (click)="onClick()">Init Page</button>
- </div>
app.module.ts
- import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
- import { BrowserModule } from '@angular/platform-browser';
- import { FormsModule } from "@angular/forms";
- import { HttpModule } from '@angular/http';
-
- import { HomePageComponent } from './src/app.component.homepage';
- import { ErrorComponent } from './src/app.component.error';
-
- @NgModule({
- imports: [BrowserModule, FormsModule, HttpModule],
- declarations: [HomePageComponent, ErrorComponent],
- bootstrap: [HomePageComponent]
- })
- export class AppModule { }
index.html
- <!DOCTYPE html>
- <html>
- <head>
- <title>Angular2 - Observables </title>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link href="../resources/style/bootstrap.css" rel="stylesheet" />
- <link href="../resources/style/style1.css" rel="stylesheet" />
- <!-- Polyfill(s) for older browsers -->
- <script src="../resources/js/jquery-2.1.1.js"></script>
- <script src="../resources/js/bootstrap.js"></script>
-
- <script src="../node_modules/core-js/client/shim.min.js"></script>
- <script src="../node_modules/zone.js/dist/zone.js"></script>
- <script src="../node_modules/reflect-metadata/Reflect.js"></script>
- <script src="../node_modules/systemjs/dist/system.src.js"></script>
- <script src="../systemjs.config.js"></script>
- <script>
- System.import('app').catch(function (err) { console.error(err); });
- </script>
- <!-- Set the base href, demo only! In your app: <base href="/"> -->
- <script>document.write('<base href="' + document.location + '" />');</script>
- </head>
- <body>
- <home-page>Loading</home-page>
- </body>
- </html>
main.t
- import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-
- import { AppModule } from './app.module';
-
- const platform = platformBrowserDynamic();
- platform.bootstrapModule(AppModule);
Now, run the code and get the output as below.