Menu

Creating angular 2 service to work with Google Places API Web Service to get address

In the recent projects I have worked on, I had the requirement to create a address auto look up feature. This is a common requirement these days, people prefer to have address auto completion as it easy to make mistakes by typing it manually. In my case I decided to go with Google Places API Web Service. Since I have worked in the past with google web services, I know how easy it is to implement.

I wrote google.places.service.ts  service in in my Angular app to call API and return the list of address matching what user have typed.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';

import { BaseService } from './base.service';
import { Config } from '../config';
import {
  GooglePlacesResponce,
  GooglePlacesAddress
} from './../models';

@Injectable()
export class GooglePlacesService extends BaseService {
  private apiUrl = 'https://maps.googleapis.com/maps/api/place/textsearch/json';

  constructor(private http: Http) {
    super();
  }

  search(query: string): Observable<any> {
    return this.http.get(this.apiUrl + "?query=" + query + "&key=" + Config.GOOGLE_PLACES_API_KEY)
      .map(this.extractData)
      .catch(this.handleError);
  }
}

yep, its that easy.

I am using to ng2-bootstap typeahead, in this sample. below is the html

<div class="form-group" [ngClass]="{'has-error':!form.controls['addressLine'].valid}">
	<label>Suburb</label>   
	<input formControlName="addressLine" 
		autocomplete="off"
		[attr.maxlength]="75" 
		[typeahead]="addresses" 
		(typeaheadLoading)="addressesLoading($event)"
		typeaheadOptionField="formatted_address"
		typeaheadWaitMs="300"
		placeholder="Address" 
		class="form-control">                               
	<template #suburbItemTemplate let-model="item">{{model.name}} {{model.postCode}} - <strong>{{model.state}}</strong></template>
	<span class="ng-typeahead-mini-loader" *ngIf="addressLoading"><i class="fa fa-cog fa-spin"></i></span>
</div>

This is how the Angular component code looks like.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Observable } from 'rxjs/Observable';

import { GooglePlacesService } from './../services/google.places.service';

import { TypeaheadMatch } from 'ng2-bootstrap/typeahead';

@Component({
    selector: 'customer-details',
    templateUrl: './details.component.html'
})

export class CustomerDetailsComponent implements OnInit {
    form: FormGroup;
    errorMessage: string;    

    public addresses: Observable<Address[]>;
    public addressSelected: string;
    public addressLoading: boolean;

    private http;

    constructor(public fb: FormBuilder,
        private route: ActivatedRoute,
        private router: Router,
        private googlePlacesService: GooglePlacesService) {

    }
	
	ngOnInit() {
		this.form = this.initForm();
        this.initSuburbLookup();
	}
	
	initForm() {
        return this.fb.group({
            customerId: [''],  
			........
            addressLine: ['', [Validators.required]],
            .....
        });
    }
	
    initSuburbLookup() {
        this.addresses = Observable.create((observer: any) => {
            this.googlePlacesService.search(this.form.value.suburb)
                .subscribe((data: any) => {
                    observer.next(data);
                });
        });
    }
	
	 public addressesLoading(e: boolean): void {
        this.addressLoading = e;
    }
}

Leave a comment