简体   繁体   English

Angular 5:无法使用异步管道更新模板

[英]Angular 5: Can't update template with async pipe

I'm learning Angular5 & RxJS at the moment. 我现在正在学习Angular5和RxJS。 I'm trying to get a simple app to run: 我正在尝试运行一个简单的应用程序:

  • Search bar to enter term 搜索栏输入术语
  • keyup triggers function that then calls a service keyup触发函数然后调用服务
  • service returns api call wrapped in observable service返回包含在observable中的api调用
  • observable is subscribed to with async pipe and template updates with results from the api call 使用api调用的结果,使用异步管道和模板更新来订阅observable

I tried two options and have one issue with each: 我尝试了两个选项,每个选项都有一个问题:

a) Subscribing in the component and update template data: a)订阅组件并更新模板数据:

this.placePredictionService.getPlacePredictions(term).subscribe( data => 
{
  this.results = data;
 });

The template does update on the {{results}} binding, but only on the second function call. 模板确实在{{results}}绑定上更新,但仅在第二个函数调用上更新。 The template then gets updated with the results from the first call. 然后使用第一次调用的结果更新模板。 Why? 为什么?

b) Returning an observable and updating template with async pipe b)使用异步管道返回可观察和更新的模板

private results$: Observable<any[]>;
this.results$ = this.placePredictionService.getPlacePredictions(term);

This way, nothing happens in the template. 这样,模板中没有任何反应。 What don't I get? 我得到了什么? Where is my understanding lacking? 我的理解在哪里缺乏? Thank you very much for giving hints on what to look into. 非常感谢您提供有关要查看的内容的提示。


Solutions to the 2 Problems: Thanks @Richard Matsen ! 解决2个问题:谢谢@Richard Matsen

a) Problem was, that the calls of the Google Maps API weren't within the Angular Zone, therefore change detection wasn't triggered automatically. a)问题是,Google Maps API的调用不在Angular Zone内,因此不会自动触发更改检测。 Wrapping the API Call in the service in the ngZone.run() function did the trick: 在ngZone.run()函数中包装服务中的API调用就可以了:

this.autocompleteService.getPlacePredictions({input: term}, data => {
    this.ngZone.run(() => {
      this.predictions.next(data);
    });

  });

b) Using a subject to not cause a new stream with every new keystroke solved the issue of the async pipe not working properly, see comment below for code. b)使用主题在每次新击键时都不会导致新流解决了异步管道无法正常工作的问题,请参阅下面的代码注释。


The full component, service & template are like this: 完整的组件,服务和模板是这样的:

app.component.ts app.component.ts

import { Component } from '@angular/core';
import { MapsAPILoader } from '@agm/core';
import { PlacePredictionService } from './place-prediction.service';

import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  private searchTerm: string;
  private results$: Observable<any[]>;

  testResult = [{description: 'test'},{description: 'test'}];

  constructor(
    private mapsAPILoader: MapsAPILoader,
    private placePredictionService: PlacePredictionService
  ){}

  onSearch(term: string){

    this.searchTerm = term;

    if (this.searchTerm === '') return;

    this.results$ = this.placePredictionService.getPlacePredictions(term);

  }

}

place-prediction.service.ts 地方-prediction.service.ts

import { Injectable } from '@angular/core';
import { MapsAPILoader } from '@agm/core';

import { Observable } from 'rxjs/Observable';

import 'rxjs/add/observable/of';
import 'rxjs/add/observable/bindCallback';

@Injectable()
export class PlacePredictionService {

  private autocompleteService;

  constructor(
    private mapsAPILoader: MapsAPILoader
  ) { 
    this.mapsAPILoader.load().then( () => {
      this.autocompleteService = new google.maps.places.AutocompleteService();
    });
  }

  // Wrapper for Google Places Autocomplete Prediction API, returns observable
  getPlacePredictions(term: string): Observable<any[]>{

    return Observable.create(observer  => {

      // API Call
      this.autocompleteService.getPlacePredictions({input: term}, (data) => {

        let previousData: Array<any[]>;

        // Data validation
        if(data) {
          console.log(data);
          previousData = data;
          observer.next(data);
          observer.complete();
        }

        // If no data, emit previous data
        if(!data){
          console.log('PreviousData: ');
          observer.next(previousData);
          observer.complete();

        // Error Handling
        } else {
          observer.error(status);
        }

      });
    });
  }

}

app.component.html app.component.html

<h1>Google Places Test</h1>
<p>Angular 5 &amp; RxJS refresher</p>
<input
  type="search"
  placeholder="Search for place" 
  autocomplete="off"
  autocapitalize="off"
  autofocus
  #search
  (keyup)="onSearch(search.value)"/> 
  <p>{{ searchTerm }}</p>
  <ul>
    <li *ngFor="let result of results$ | async "> {{result.description}}</li>
  </ul>

A manual call to ChangeDetectorRef.detectChanges fixes the event lagging. 手动调用ChangeDetectorRef.detectChanges修复事件延迟。

I guess the api call is outside of Angular's automatic change detection, so it needs to be triggered each time new results arrive. 我猜api调用不在Angular的自动变化检测之内,因此每次新结果到达时都需要触发。

place-prediction.service.ts 地方-prediction.service.ts

@Injectable()
export class PlacePredictionService {

  predictions = new Subject();
  private autocompleteService;

  constructor(
    private mapsAPILoader: MapsAPILoader
  ) {
    this.mapsAPILoader.load().then( () => {
      this.autocompleteService = new google.maps.places.AutocompleteService();
    });
  }

  // Wrapper for Google Places Autocomplete Prediction API, returns observable
  getPlacePredictions(term: string) {

    // API Call
    this.autocompleteService.getPlacePredictions({input: term}, (data) => {
      this.predictions.next(data);
    });
  }
}

app.component.ts app.component.ts

import { Component, ChangeDetectorRef } from '@angular/core';
...

export class AppComponent  {

  private searchTerm: string;
  private results = [];

  constructor(
    private cdr: ChangeDetectorRef,
    private mapsAPILoader: MapsAPILoader,
    private placePredictionService: PlacePredictionService
  ){}

  ngOnInit() {
    this.placePredictionService.predictions.subscribe(data => {
      this.results = data;
      this.cdr.detectChanges();
    });
  }

  onSearch(term: string) {
    this.searchTerm = term;
    if (this.searchTerm === '') { return; }
    this.placePredictionService.getPlacePredictions(term);
  }
}

app.component.html app.component.html

<ul>
  <li *ngFor="let result of results"> {{result.description}}</li>
</ul>

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

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