Showing posts with label AngularJS. Show all posts
Showing posts with label AngularJS. Show all posts

Sunday, May 14, 2017

Angular v4 (or) call it just "Angular"

This blog is a high level gist of Angular 4 features. For detailed explanation refer to my article in DNC magazine. Article is titled "Angular 4 application development with Bootstrap 4 and TypeScript". Follow this link to download the magazine for free.


In December 2016, in an NG-BE (Angular Belgium) keynote, Igor Minar talked about Angular’s release schedule and upcoming features. In the process, he announced plan for a major release in March 2016 - “Angular 4”. Angular team decided to skip a major version number 3.

Why not Angular 3? - MonoRepo: Angular 2 has been a single repository, with individual packages downloadable through npm with @angular/package-name convention. For example @angular/core, @angular/http, @angular/router so on. Considering this approach, it’s important to have consistent version numbers among various packages. Angular router has already been on version 4. Hence, Angular team skipped a major version 3. It will help avoid confusion with certain parts of the framework on version 4 and the others on version 3.

If there were any apprehensions about Angular 4, could be due to baggage of scale of transition between Angular 1.x and 2.x. Considering Angular was moving from MV* (Model View Whatever) pattern to components focused approach, the framework features were very different and caused many applications to rewrite major parts of their code base.


However, between v2.x and v4 it is a very different story. It is a progressive enhancement. Majority of changes are non-breaking. 

Angular 4 is out on 23rd March ’17. Consider the following enhancements,
  • The release has considerable improvements in bundle size. Some have reported up to 60% reduction in Angular bundles’ file size.
  • The ngc, AOT compiler for Angular and TypeScript is much faster.
  • Angular 4 is compatible with TypeScript’s newer versions 2.1 and 2.2. TypeScript release helps with better type checking and enhanced IDE features for Visual Studio Code. The changes helped the IDE detect missing imports, removing unused declarations, unintentionally missing “this” operator etc.

Get Started with an Angular 4 project using Angular CLI

The latest version of Angular CLI (v1.0) is already scaffolding with Angular 4. If you are using an older version, upgrade Angular CLI. A new project created using Angular CLI references Angular 4. Refer to figure 1. 

To upgrade Angular CLI, run following commands,

npm uninstall -g angular-cli
npm cache clean
npm install -g angular-cli@latest

To create a new project with Angular CLI run the following command
ng new my-project-name

Figure 1 Version details for various libraries with ng -v command

What are the new features in Angular 4?

Note: Refer to this link for code samples.  These samples were built for article in DNC magazine. You may refer to the detailed article in the magazine

Template changes to ng-template

If you are upgrading from Angular 2.x to Angular 4, all template elements have to change to ng-template. Following code will result in a warning.
  <template [ngIf]="isVisible"> Conditional statement </template>

Template parse warnings: The <template> element is deprecated. Use <ng-template> instead

Refactor it to
<ng-template [ngIf]="isVisible"> Conditional statement </ng-template>

Angular *ngIf/then/else

With Angular 2 we could use *ngIf directive to conditionally show a section of the template. With Angular 4, support for else has been added. Consider the following template code
<div *ngIf="isTrue; else whenFalseTmpl">
      <span>I show-up when isTrue is true.</span>
</div>

<ng-template #tmplWhenFalse > I show-up when isTrue is false </ng-template>

When isTrue value is true, instead of showing the span inline, we could offload to another template.
<button class="btn btn-primary" (click)="toggle()"> Toggle </button>
<div *ngIf="isTrue; then tmplWhenTrue else tmplWhenFalse"></div>
<ng-template #tmplWhenTrue >I show-up when isTrue is true. </ng-template>
<ng-template #tmplWhenFalse > I show-up when isTrue is false </ng-template>

Working with Observables and *ngIf/else

While rendering an observable on a template, we can show loading message or a spinner gif with the *ngIf directive. Specify else template to show while async observable is not ready with data. The directive also supports creating a local variable. Notice a local variable dino (let dino) to refer the async object. Consider following code.
<!—show else template “working” while loading observable with data. Notice async filter for dino observable -->

  <div *ngIf="dino | async; else working; let dino">
    <div class="card col-8">
      <div class="col-4">
              <img class="card-img-top" src="assets/images/{{dino.name}}.png" [alt]="dino.name">
      </div>
      <div class="card-block">
        <h4 class="card-title">{{dino.name}}</h4>
<!--removing dinosaur card details for readable snippet. Refer to code sample for complete code. -->
    </div>
    <!-- card end -->
  </div>
  <ng-template #working>
    <div>Loading...</div>
  </ng-template>

In the sample, to mimic delayed loading, the component calls next() on an observable subject after four seconds. Refer to following code snippet,
    this.dino = new Subject<any>();
    // to mimic delayed loading the component calls next on observable subject after four seconds. 
    setTimeout( () =>
     this.dino.next(dataset.dinosaurs[Math.round((Math.random() * 5))])
    , 4000);

Angular Animations

In Angular 2.x, animations were part of @angular/core. It was part of the bundle even if application doesn’t use animations. With Angular 4, animations related artifacts have been moved out of @angular/core. It helps reduce production bundle size. To use animations import BrowserAnimationsModule from @angular/platform-browser/animations. Reference the module in imports array of @NgModule

Angular future release schedule

Refer to the major release schedule below. The details are quoted from this link. Please keep a tab on the page for any changes to the schedule.

Tentative Schedule after March 2017

Date
Stable Release
Compatibility*
September/October 2017
5.0.0
^4.0.0
March 2018
6.0.0
^5.0.0
September/October 2018
7.0.0
^6.0.0
Release schedule taken from AngularJS blog https://github.com/angular/angular/blob/master/docs/RELEASE_SCHEDULE.md

Conclusion

Angular went through good amount of transition from Angular 1.x, MV* model to the framework we know today. The purpose of the transition is to effectively support new features in JavaScript. When used with TypeScript, it is that much more powerful with support for types, integration with IDE like Visual Studio Code and many other features.

In the process, Angular 2 upgrade included many breaking changes. However, upgrading to future versions of Angular are expected to be smooth with minimal or no breaking changes. Framework will continue to evolve to support more features and make developers job easy. It is a good thing. Obviously we do not want a stagnated framework.

References and useful links

Refer to this link for code samples used for the article in the DNC Magazine.
DotNet curry website - http://www.dotnetcurry.com/
For more about Angular CLI NPM repo with documentation, follow the link https://www.npmjs.com/package/angular-cli
Following the link for Angular 4 release notes - http://angularjs.blogspot.in/2017/03/angular-400-now-available.html


Saturday, October 8, 2016

Getting started with AngularFire 2

The blog describes using AngularFire2, Angular 2 and TypeScript API for Firebase. It is a beginner guide with a sample for retrieval and update to Firebase database.
Blogger: V. Keerti Kotaru . Author of Angular Material book . Twitter @KeertiKotaru . linkedin.com/in/keertikotaru

Firebase started as a cloud database that can store and retrieve JSON objects. It provided an effective database solution for mobile apps and other applications. Today if you look at the newer version of Firebase, database is only a part of it. It has great analytics features, push notifications to mobile & Chrome and Firebase Cloud Messaging (FCM).

Realtime Database

Firebase database has a unique feature to synchronize database and clients systems automatically. In case of Web UI, it uses a Web Socket connection to push changes to the client. As and when there is a change to the JSON data on the cloud DB, it is pushed to all connected clients.

Sample - Bus Schedule Management: For the blog, I built a couple of pages that deal with bus schedule management. A page to list buses and schedule. And another page to update if there is delay in the arrival time for a bus. 

Consider figure 1. The window on the left shows list of buses to the passengers. The window on the right could be used by admins to update if there is a delay in arrival. The sample is using AngularFire2 (Firebase API for Angular2 & Typescript) to connect with the Firebase database. As and when there is a change to ETA (Expected Time of Arrival), it's instantly synchronized with all clients.

Checkout complete code sample here..


Figure 1: Real time updates
Figure 2: A Sample Firebase App
Left Nav with feature List.

This blog uses AngularFire 2 & Typescript for the Firebase API. Firebase integrates with multiple platforms including Android, iOS and Web. While the Web JavaScript API is for plain JS that could be use in any HTML/JS app, the AngularFire is AngularJS specific API.

AngularFire 2 is in beta at the time of writing this blog. It uses Angular 2. And code in this blog uses TypeScript along with Angular.

Getting Started - Create an app on Firebase console

  • Log into firebase at firebase.google.com. Sign-up if you don't have an account already.
  • Once logged-in,  click on "Go to console".
  • It lists an existing Firebase apps. If it's a new account create an app. 
  • Click on the app to see various features provided by Firebase.

Create Angular 2 Project

Angular CLI is preferred tool for scaffolding an Angular 2 application. Create a new Angular2 app using the following command. It will also install the dependencies.


ng new angular-fire-2-sample

Note: If you do not have Angular CLI already installed, follow the link to get instructions on installing the tool. 


Add angularfire2 and firebase package references


To use AngularFire2 and firebase API in the project, install the package using the following command.


npm install firebase angularfire2 --save
(--save will update to package.json for future installation of required dependencies)


Add AngularFire2 app to Angular Module

In the scaffolded project the main module is in the file  src/app/app.module.ts
Edit this file to import AngularFireModule from angulafire2
import {AngularFireModule} from 'angularfire2';


Create configuration object. 

// API documentation suggests to export the configuration
export const config = { 
 apiKey: "[API Key]", 
 authDomain: "gdg-bustracker.firebaseapp.com", 
 databaseURL: "https://gdg-bustracker.firebaseio.com", 
 storageBucket: "" 
};

Follow below steps to create the configuration object readymade.
  1. Click on settings icon next to the app in Firebase Console.
  2. Click on "Add Firebase to you Web App" link. It presents the configuration object.
  3. Copy the configuration to the AngularFire app.
Figure 3: Two easy steps to get to Firebase configuration to be added to the Web App
Add reference to the module in the module imports. Consider following code,
@NgModule({ 
 declarations: [ AppComponent ], 
 imports: [ 
       BrowserModule, 
       FormsModule, 
       HttpModule, 
       AngularFireModule.initializeApp(config) // initializes and import Firebase module     ], 
 providers: [], 
 bootstrap: [AppComponent] 
})

Note: There is an open bug, that could result in build errors with firebase package. Add the following line in src/main.ts to circument the problem temporarily.

import * as firebase from 'firebase';

Now Firebase API is ready to use.

Create a service to integrate with Firebase

It is a good idea to keep the bus data access in a separate service. It could be injected in components while dealing with bus data.

Create the service using Angular CLI with the following command.

ng g service bus-data-access

The generated class is named BusDataAccessService. Provide the service in the main module (app.module.ts). Consider following code snippet,
// import the service module 
import { BusDataAccessService } from './shared/bus-data-access.service'; 

// add it to providers list in the module. Note that the @NgModule decorator is stripped off additional details for readability. Look at the file in github for complete code. @NgModule({ 
 declarations: [ ], 
 imports: [ AngularFireModule.initializeApp(config) ], 
 providers: [ BusDataAccessService], // *** the service is provided here (to the module)
 bootstrap: [AppComponent] }) 
export class AppModule { }

Import the following in the newly created bus-data-access.service.ts file,
import { AngularFire, FirebaseListObservable } from 'angularfire2';
  • AngularFire - provides API for various firebase services. In the BusDataAccessService, we use it to interact with database.
  • FirebaseListObservable - An RxJS observable. The BusDataAccessService returns the observable (list of buses) which could used in the template. Please note, the bindings in the template are asynchronous with observables.

Retrieve the bus list

Consider following code in the BusDataAccessService for retrieving bus list.
// Class property for bus list 
 buses: FirebaseListObservable; 

 // inject AngularFire service 
 constructor(firebase: AngularFire) { 
   // get bus list from schedule node on the Firebase DB. 
   this.buses = firebase.database.list("/schedule"); 
}

We are retrieving bus list from a node named schedule in the JSON stored on Firebase DB. Here is the structure of bus object I have. The FirebaseListObservable object yields a list of these objects.
"A2F001": { 
   "from": "Hyderabad", 
   "to": "Bengaluru", 
   "expectedTimeOfArrival": "10/02/2016 10:00", 
   "scheduledTimeOfArrival": "10/02/2016 10:00", 
   "delay": 0, 
   "delayReason": "N/A" 
 }

A function getBusList on BusDataAccessService returns the observable.
getBusList(){ 
  return this.buses; 
}


Update changes to Bus Schedule

We use another function in BusDataAccessService for updates to the bus schedule. Consider following code.
  saveBusData(id, expectedTimeOfArrival, delay, delayReason){
    this.buses.update(id, {
      expectedTimeOfArrival:expectedTimeOfArrival,
      delay:delay,
      delayReason: delayReason
    })
  }

In the given application I anticipate changes to three properties, expected time of arrival, delay in minutes and delay reason. The save function expects these as parameters.

We also need to know which bus schedule is being updated. The first parameter, id should have the unique identifier for the bus JSON object.

The update API (in AngularFire) expects field being updated as a key and the new value as the value. We are using ES2015 syntaxes. When the key and value variable names are the same (on the JSON object), we don't have to write them twice. Will cleanup this payload as the following.


  saveBusData(id, expectedTimeOfArrival, delay, delayReason){
    this.buses.update(id, {
      expectedTimeOfArrival,
      delay,
      delayReason
    })
  }


Integrate the BusDataAccessService with the components

Component shows the bus data on the screen. Component calls the above written getBusList() of BusDataAccessService.

As a first step, import the service in bus-list.component.ts
import { BusDataAccessService } from '../shared/bus-data-access.service';

In the constructor inject the service and call getBusList function. The returned data is assigned to a field buses on the class.
  constructor(dataService: BusDataAccessService){
    this.buses = dataService.getBusList();
  }

In the template iterate through buses asynchronously. Refer to async filter on the *ngFor. Note that the field buses is an observer. Unlike an array, whole list is not available in an observer upfront. Each record is asynchronously obtained.

Consider following template snippet from bus-list.component.html 
<div *ngFor="let item of schedule | async">
        <h3 class="panel-title">{{item.from}} to {{item.to}}</h3>
        <!-- other similar bindings go here. Refer to file in github for complete template. -->
</div>


Make updates to bus data

 In the sample repo, another component admin has controls to update delay information. Refer to figure 1, which let's user add delay in minutes and reason for the delay. The admin component has similar template to that of bus list, with additional controls to increment/decrement minutes and a text area to input reason for the delay.

As user updates delay information and clicks on the save button, following handler is called in the component class. It in-turn calls the saveBusData function in BusDataAccessService, which is using the Firebase API to update the database.
   save(item){
    this.busDataAccess.saveBusData(item.$key,
      item.expectedTimeOfArrival,
      item.delay,
      item.delayReason
    );
   }

Template for the button in admin component
<button class="btn btn-info" (click)="save(item)">Save</button>

References and further reading

AngularFire 2 Github
Follow this link for complete code sample
Firebase Docs

Thursday, September 15, 2016

Working with Grid List in Angular Material


This blog describes using Angular Material's Grid List to show an array of data. We use Angular Material directives/elements/attributes for rendering the content.



Angular Material’s Grid List provides a different perspective to the regular list control. Each item in the grid list are laid out as tiles. It provides additional space and an elaborate view. More than anything, it is fun to play with the layout compared to regular list.

For the blog I’m using dinosaur data represented with a grid list. Special thanks Firebase sample dataset that provide readymade JSON objects. (Not using firebase for this sample. Just used the sample dataset). Refer to references section at the end for a link to the dataset.

Getting Started 

 Use md-grid-list and md-grid-tile directive to create a Grid List. Consider following code for md-grid-list.

  • Use md-cols attribute for configuring number of columns on the grid list. 
  • Use md-row-height attribute to set height of each row (and hence the tile). 
<md-grid-list md-cols="4" md-row-height="200px">…</md-grid-list>

Each tile on the grid list is represented by md-grid-tile directive. Use ng-repeat to iterate through array of dinosaur data. 
<md-grid-tile ng-repeat="item in dinosaurs" > ... </md-grid-tile>

Within md-grid-tile, use elements/directives md-grid-tile-header and md-grid-tile-footer elements to add header and footer to each tile. Blog's sample is using a footer. Consider the following code. It shows two elements on the footer for a dinosaur, name and the order.

<md-grid-tile-footer >
          <strong>{{item.name}}</strong>
          <div>{{item.order}}</div>
  </md-grid-tile-footer>

Responsive Attributes

A four column grid looks good on a desktop screen. How about a mobile or tablet screen? The tiles might squeeze and the content might not be legible. Use Angular Material attributes that take advantage of CSS3 Flexbox break points for rendering according to the screen size. Consider following sample.

   <md-grid-list md-cols-gt-sm="4" md-cols-sm="2" md-cols="1" md-row-height="200px">

Use md-cols-sm with a value 2.  It shows grid list with two columns on a small screen. The CSS Flexbox (used underneath by Angular Material) considers screen width between 600px and 960px as a small screen.

Use md-cols-gt-sm with a value 4. It shows grid list with four columns on a screen greater than large. That is medium, large and extra large screens. Anything with screen width greater than 960px is considered greater than small.

That leaves an extra small screen (screen width less than 600px). Use md-cols default value to 1. It shows a grid list with one column on an extra small screen.


Make specific tiles larger

Based on a specific criteria, one or more
tiles could be made larger than others. Consider following sample. It makes the first tile span over two rows. $index represents index of an item in the loop with ng-repeat. If it’s 0, set rowspan  value to 2. Otherwise stick to default value 1.

<md-grid-tile md-rowspan-gt-xs="{{($index===0)?2:1}}" ng-class="item.background" ng-click="null" ng-repeat="item in dinosaurs">

It is a simplistic example. But consider using the rowspan based on tile's content length. For tiles with larger content or images, increase the rowspan.

Also, notice -gt-xs break point has been used on md-rowspan. As detailed out already, on an extra small screen, grid list shows a single column. With nothing next to it in a row, we can set it to default height on an extra small screen.

Complete Sample


References:

Angular Material website (https://material.angularjs.org) for

  • Grid List details.
  • Responsive break points.

Firebase dinosaurs sample dataset- https://dinosaur-facts.firebaseio.com/dinosaurs

Wednesday, June 29, 2016

Typeahead search with Angular2 and TypeScript


This blog describes typeahead search implementation using Angular 2 and TypeScript. The functionality allows user to start typing in a form field and provides list of recommendations with the given text. See my previous blogs for Angular 1.x implementation and Introduction to RxJS in Angular 2. 



Type ahead search: 

Off late, one of the common feature in forms (on a web page) is type ahead search. It is generally tied to text fields and drop-downs. The functionality allows user to start typing in a form field and it provides list of recommendations with the given text.

The implementation is relatively simple for fields like country list or states list. We can download the complete list in the browser and filter through while user is typing. However, if it is a stock ticker, we might need to retrieve data dynamically from a server API. Here, efficiently managing the server calls is critical.


Reactive Extensions - JavaScript : 

RxJS (Reactive Extensions JavaScript) and observables are quite effective with requirements like typeahead search.

RxJS is nothing but,
  1. Observables -  asynchronously process stream of data. The source of the stream could be network resources (Server APIs) or UI controls. Observable emits items in a stream and an observer processes them.
  2. Operators - that act on emitted sequence of items. Examples: 
      1. map() for transforming results
      2. take(n) for selecting first n items from an observable
  3. Scheduler - for scheduling and decision making on when to emit an item in an observable.

My previous two blogs(1 & 2) discussed RxJS and Observables. Type ahead search is even a better example for the following reasons and complexity
  • A continuous stream of events are emitted from the UI - user types characters in the text field resulting in new information each key press.
  • Potentially each key press can result in server side API call to retrieve new information. Handle results from the API calls asynchronously. 


Implementation: 

Consider following component code,

 @Component({
    selector: 'typeahead-search',
    templateUrl: 'app/templates/search.tpl.html'
 })
 export class AppComponent
 {
    searchForm: any;
    results: Observable<any>;  
 
    constructor(private fb: FormBuilder, private http: Http){
        this.searchForm = this.fb.group({
            'searchField': [''] // we can set default value - it is set to be an empty string here.
        });

        // reference the control (ngControl)
        var ctrl = this.searchForm.controls.searchField;



        this.results = ctrl.valueChanges
                    .switchMap(fieldValue => this.http.get(`http://localhost:3001/api/search?term=${fieldValue}`))
                    .map(res => res.json());

 }


We are creating an element/component named typeahead-search. We have a single text field in the form. Form Builder API helps create Control Group, set up validations etc. For simplicity, we are not adding any validations for the typeahead text field in the sample.

Consider following line of code. This line is at the heart of type ahead search. That's all it takes to implement typeahead search in Angular 2. 

        
this.results = ctrl.valueChanges
                    .switchMap(fieldValue =>          this.http.get(`http://localhost:3001/api/search?term=${fieldValue}`))
                    .map(res => res.json());

  • On the control (text field) valueChanges is an observable<any>. It emits new text as user is typing. It emits new text.
  • On the observable, the switchMap() operator accepts each value emitted and passes it on to the callback. The emitted values are latest search string on the control. The callback function makes an HTTP call using http.get API. 
  • The returned object is transformed to desired result using another RxJS operator map. 

Debounce: debounceTime(timeInMilliseconds)

It is another useful RxJS operator. It restricts emitting items from an observable only after the given timespan. It drops events till the given time and emits the latest. For example, if we use it on valueChanges observable in the above example, next value is emitted only after given milliseconds. Only the latest is emitted. That is, as user is typing text, if we set debounce to 500 milliseconds, newer text is returned (emitted) at 500 milliseconds interval (while user is typing). This reduces number of API calls to the server and makes the whole thing manageable. Consider following code and images


        this.results = ctrl.valueChanges

                    .debounceTime(500)
                    .switchMap(fieldValue => this.http.get(`http://localhost:3001/api/search?term=${fieldValue}`))
                    .map(res => res.json());




Code Sample

For the complete code sample, follow the link

References

Wednesday, March 16, 2016

Typeahead search with RxJS in AngularJS applications

This blog aims to demonstrate concept of Observables in AngularJS. I'm using Typeahead search as an example. It describes implementing Typeahead search in Angular 1.x

Introduction

RxJS - Reactive Extensions JavaScript is dealing with streams of data asynchronously. Observables is one of the important aspects of RxJS. Observable results in stream of data.  Many times, for understanding Observables they are compared to Promises. Both deal with asynchronous actions. Difference is unlike Observable, Promise is done once the asynchronous operation is complete. Consider a Http call. Promise is complete once response is obtained (or the call errors out). With Obserables data or items are emitted continuously like a stream.

Where do we see stream of data in JavaScript, especially in a browser? I could think of couple of examples,
  1. Data bound to UI controls: As user interacts with the UI, edits a text field, selects an option in the drop down, and continues to do so, there are series of changes emitted from the control.
  2. Web Socket - a persistent connection. Server could send a stream of data. As long as the connection is open, browser (or other clients) could continue to obtain new pieces of data. Unlike a XHR, it's not done once we receive response soon after establishing connection.
Another difference between Promises and Observables is that, Observables can be unsubscribed (cancelled). 

In the blog, I'm planning to write about "Typeahead search". Here the UI control is an Observable. It emits data items. As user starts keying in search term into the text box results are updated. It triggers XHR request with each change or set of key strokes in the text field. Show results using the latest response. Cancel in-progress, old and obsolete XHRs.

Sample is using AngularJS (v1.x) and RxJS libraries to code this functionality.

Run with Angular 1.x

bower install angular angular-rx

Reference the libraries
  <script src="bower_components/rxjs/dist/rx.lite.min.js"></script>        
  <script src="bower_components/angular/angular.min.js"></script>
  <script src="bower_components/angular-rx/dist/rx.angular.js"></script>

Bootstrap Angular module with "rx" module as a dependency.
angular.module("typeAheadSample", ["rx"]);

Consider the following template for text field. This is where user keys in the search term.
<input type="text" ng-model="searchString" ng-change="search()">

Notice we are calling "search()" controller function (on $scope) for changes detected with the text field. In the controller inject "rx" service for creating an Observable and $http for making API calls.

Create an observable function using rx service API,
var searchControlObservable= $scope
          .$createObservableFunction('search');

RxJS has galore of operators (which are functions in JS). Before I describe one such operator, remember an Observable emits items or data. In our example as and when user keys in values into text field, observable is emitting the text to all observers (subscribers). 

Each search term results in search result (from the API). And we have series of results. That means we have Observables of Observables.

Consider following code,
 searchControlObservable = searchControlObservable
         .debounce(500)// Observable holds off 500 milliseconds before emitting data.
         .flatMapLatest(function(term){
             return rx.Observable                                
                   .fromPromise($http({
                      url: "http://localhost:3001/api/search?term=" + $scope.searchString,
                      method: "get"                                
                    }))
                   .map(function(response){                                
                       return response.data;    
                    });
               });

The callback for flatMapLatest is invoked for every emitted item of searchControlObservable (which is result of text field change events). This in turn returns Observables out of XHR calls. These are Observables from promises.

flatMap is an operator, useful in such scenarios where we have Observables of Observables. It transforms and merges. A variant of flatMap is flatMapLatest. In our scenario only the latest search result matters. Previous ones could be ignored. So it stops or unsubscribes from previous Observables. As stated earlier unlike Promises, Observables could be stopped or unsubscribed from.

Also notice debounce(500); It will hold off emitting items for 500 ms. This will help control number of XHR calls. We can increase / decrease the number depending on the requirement, acceptable limit for load on the API etc. Ideally we want search calls made for considerable text keyed-in, instead of every little change.

With debounce

Without debounce























And finally, map returns transformed object. 

Subscribe to receive items emitted by Observable,
  .subscribe(function(results) {
                            $scope.$apply(function(){
                                $scope.messages = results;  
                            });
                        });      

Note: Subscribe function accepts three callbacks or another Observer instance. i) Above example has onNext handler (which receives items emitted by Observer). ii) Error handler iii) onCompleted handler. If an Observer object is passed, its callbacks are invoked.

Loop through the response to show the list of ,
<div ng-repeat="message in messages track by $index">
    <div>{{message.name}}</div>
</div>

Note: The API in the sample (http://localhost:3001/api/search?term=) is a dummy node API that reads from a file and returns results. It's a quick search service for demonstration purposes.          

Follow the link for complete code sample. Follow instructions in ReadMe to download and run the sample.

References