简体   繁体   English

Angular2中带有Observables的Google地方信息

[英]Google Places with Observables in Angular2

I try to use Google Places with Observables in Angular 2. 我尝试在Angular 2中将Google地方信息与Observables结合使用。

To do that, I included the Google scripts in the index.html and then I get some inspiration with Observables from http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html 为此,我将Google脚本包含在index.html中 ,然后从http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in中获得了Observables的启发。 -angular2.html

<!-- Script included in index.html -->
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"></script>

You can see the whole application there: https://embed.plnkr.co/LQaag2/ 您可以在此处看到整个应用程序: https : //embed.plnkr.co/LQaag2/

I think there is an issue with the events. 我认为这些事件有问题。 For example, when the user type "P", nothing appears. 例如,当用户键入“ P”时,什么也不显示。 But if he clicks on the page or he types "a", then he will see the results of places starting by "P". 但是,如果他单击页面或输入“ a”,那么他将看到以“ P”开头的地点的结果。

Do you have an idea why? 你知道为什么吗?


app/main.ts 应用程序/ 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/app.module.ts 应用程序/ 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/app.component.ts 应用程序/ app.component.ts

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

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

app/app.component.html 应用程序/ app.component.html

<google-search></google-search>

app/google-place.ts 应用程序/谷歌place.ts

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

app/google-search.component.ts 应用程序/谷歌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));
  }
}

app/google.service.ts 应用程序/ 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);
      }
    });
  }
}

don't know if you're still interested but I was facing the same issue today with the bootstrap typeahead. 不知道您是否仍然有兴趣,但是今天我遇到了同样的问题,即使用了bootstrap typeahead。 I think I found a solution although I don't think it's the way one should do it. 我认为我找到了解决方案,尽管我不认为这是应该这样做的方法。 Anyway, my approach was to gather the data and let the data display as if it was static. 无论如何,我的方法是收集数据并让数据显示为静态。

  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);
});
}

Then you sould be able to access the data as soon as it is available. 这样,您就可以立即访问可用的数据。

I just had a very similar problem with google maps. 我只是有一个非常相似的问题,谷歌地图。 I will share here my answer, all the same, although it is so late. 我会在这里分享我的回答,尽管太晚了。

The problem is because the callback function displaySuggestions of the google maps getQueryPredictions is called outside of the 'angular zone', and so angular doesn't correctly detect the changes inside of it. 问题是因为Google地图getQueryPredictions的回调函数displaySuggestions在“角度区域”之外被调用,因此角度无法正确检测其内部的变化。

The solution is relatively simple. 解决方案相对简单。 Just 4 little changes to the app/google.service.ts. 对app / google.service.ts进行4个小的更改。 See the comments. 查看评论。

// 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);
      }
    });
  }
}

Edit: Perhaps you should take out your API key from the plunker, although i suppose that it might not be to serious of a problem, if it is a free one and was created exclusively for the purpose of the example... 编辑:也许您应该从插件中取出您的API密钥,尽管我认为这不是一个严重的问题,如果它是免费的,并且仅出于示例目的而创建的...

I found an awful solution. 我找到了一个糟糕的解决方案。 In app/google-search.component.ts , I've added the following function : app / google-search.component.ts中 ,我添加了以下功能:

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

Then in the ngOnInit function, I call recursiveTimeout : 然后在ngOnInit函数中,我调用recursiveTimeout

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

With this solution, when the user type "P" (for example): 使用此解决方案,例如,当用户键入“ P”时:

  1. The result will be fetched on the Google API 结果将在Google API上获取
  2. The result will be displayed just after the event recursiveTimeout is triggered (maximum 1000 ms) 结果将在触发事件recursiveTimeout后立即显示(最长1000 ms)

I am open to any better solution ;) 我愿意接受任何更好的解决方案;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM