Angular2中带有Observables的Google商家信息

时间:2016-10-03 12:22:36

标签: google-maps angular

我尝试在Angular 2中使用Google Places和Observables。

为此,我在 index.html 中添加了Google脚本,然后从http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

获得了Observables的一些灵感。
<!-- Script included in index.html -->
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"></script>

您可以在那里看到整个应用程序:https://embed.plnkr.co/LQaag2/

我认为事件存在问题。例如,当用户键入“P”时,不显示任何内容。但如果他点击页面或输入“a”,他就会看到以“P”开头的地方结果。

你知道为什么吗?

的应用程序/ main.ts

import { platformBrowserDynamic }    from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/switchMap'

platformBrowserDynamic().bootstrapModule(AppModule);

的应用程序/ app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { JsonpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { GoogleSearchComponent } from './google-search.component'
import { GoogleService } from './google.service';

@NgModule({
  imports: [BrowserModule, JsonpModule, ReactiveFormsModule],
  declarations: [AppComponent, GoogleSearchComponent],
  providers: [GoogleService],
  bootstrap: [AppComponent]
})
export class AppModule {}

的应用程序/ app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: 'app/app.component.html'
})
export class AppComponent { }

的应用程序/ app.component.html

<google-search></google-search>

的应用程序/谷歌place.ts

export class GooglePlace {
  constructor(public id: string,
              public description: string
  ) {}
}

的应用程序/谷歌search.component.ts

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { GoogleService } from './google.service';
import { GooglePlace } from './google-place';

@Component({
  selector: 'google-search',
  template: `
    <div>
      <h2>Google Search</h2>
      <input type="text" [formControl]="term">
      <ul>
        <li *ngFor="let item of items | async">{{item.description}}</li>
      </ul>
    </div>  
  `
})
export class GoogleSearchComponent {

  items: Observable<Array<GooglePlace>>;
  term = new FormControl();

  constructor(private googleService: GoogleService) {}

  ngOnInit() {
    this.items = this.term.valueChanges
                 .debounceTime(400)
                 .distinctUntilChanged()
                 .switchMap(term => this.googleService.search(term));
  }
}

的应用程序/ google.service.ts

import { Injectable } from '@angular/core';
import { GooglePlace } from './google-place';
import { Observable } from 'rxjs/Observable';

declare var google: any;

@Injectable()
export class GoogleService {      
  search(term: string) {
    return new Observable<GooglePlace[]>(observer => {
      let result: GooglePlace[] = [];
      let displaySuggestions = function(predictions: any, status: string) {
        if (status != google.maps.places.PlacesServiceStatus.OK) {
          alert(status);
          return;
        }
        predictions.forEach(function(prediction: any) {
          result.push(new GooglePlace(prediction.place_id, prediction.description));
        });
        observer.next(result);
        observer.complete();
      };
      if (term) {
        let service = new google.maps.places.AutocompleteService();
        service.getQueryPredictions({ input: term }, displaySuggestions);
      }
    });
  }
}

3 个答案:

答案 0 :(得分:1)

不知道你是否仍然感兴趣,但今天我在使用bootstrap typeahead遇到同样的问题。我想我找到了一个解决方案,虽然我不认为这是应该这样做的方式。 无论如何,我的方法是收集数据并让数据显示为静态数据。

  ngOnInit(): void {
//this.recursiveTimeout();
this.items = this.searchTermStream
  .debounceTime(300)
  .distinctUntilChanged()
  .switchMap((term: string) => this.placesService.search(term))

  .catch(() => {
      this.searchFailed = true;
      return Observable.of([])
    }
  )
this.items.subscribe(res => {
  this.places = res; 
  //places is a string array and stores all found places , in your case it 
    would be an array of GooglePlace

  console.log(this.places);
});
}

然后,您可以在数据可用时尽快访问。

答案 1 :(得分:1)

我与谷歌地图有一个非常相似的问题。我会在这里分享我的答案,尽管已经很晚了。

问题是因为google地图getQueryPredictions的回调函数displaySuggestions在'angular zone'之外调用,因此angular无法正确检测其内部的变化。

解决方案相对简单。只需对app / google.service.ts进行4次小修改即可。见评论。

// import NgZone
import { Injectable, NgZone } from '@angular/core';
import { GooglePlace } from './google-place';
import { Observable } from 'rxjs/Observable';

declare var google: any;

@Injectable()
export class GoogleService {
  // Inject NgZone in the constructor
  constructor(private _ngZone: NgZone) {}
  search(term: string) {
    // save 'this' to a constant or alternatively bind it to the callback function
    const self = this;
    return new Observable<GooglePlace[]>(observer => {
      const result: GooglePlace[] = [];
      const displaySuggestions = function(predictions: any, status: string) {
        if (status !== google.maps.places.PlacesServiceStatus.OK) {
          console.log('GoogleService search: ', status);
          return;
        }
        // Wrap the prediction in the zone
        self._ngZone.run(function() {
          predictions.forEach(function(prediction: any) {
            result.push(
              new GooglePlace(prediction.place_id, prediction.description)
            );
          });
          observer.next(result);
          observer.complete();
        });

      };
      if (term) {
        const service = new google.maps.places.AutocompleteService();
        service.getQueryPredictions({ input: term }, displaySuggestions);
      }
    });
  }
}

编辑:也许你应该从plunker中取出你的API密钥,虽然我认为它可能不是一个严重的问题,如果它是一个免费的,并且是专门为了示例的目的而创建的...

答案 2 :(得分:0)

我找到了一个糟糕的解决方案。在 app / google-search.component.ts 中,我添加了以下功能:

recursiveTimeout(ms: number = 1000): void {
  setTimeout(() => {
    this.recursiveTimeout(ms);
  }, ms);
}

然后在ngOnInit函数中,我调用recursiveTimeout

ngOnInit(): void {
  this.recursiveTimeout();
  // ...
}

使用此解决方案,当用户键入“P”(例如):

  1. 结果将在Google API上提取
  2. 结果将在事件recursiveTimeout被触发后显示(最长1000毫秒)
  3. 我愿意接受任何更好的解决方案;)