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

Posted

技术标签:

【中文标题】Angular 5:无法使用异步管道更新模板【英文标题】:Angular 5: Can't update template with async pipe 【发布时间】:2018-07-18 03:31:12 【问题描述】:

我现在正在学习 Angular5 和 RxJS。我正在尝试运行一个简单的应用程序:

搜索栏输入术语 keyup 触发函数,然后调用服务 服务返回封装在 observable 中的 api 调用 observable 订阅了异步管道和模板更新以及来自 api 调用的结果

我尝试了两种选择,但每种都有一个问题:

a) 订阅组件并更新模板数据:

this.placePredictionService.getPlacePredictions(term).subscribe( data => 

  this.results = data;
 );

模板确实在 results 绑定上更新,但仅在第二个函数调用时更新。然后使用第一次调用的结果更新模板。为什么?

b) 使用异步管道返回可观察和更新的模板

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

这样,模板中不会发生任何事情。我没有得到什么?我的理解不足在哪里?非常感谢您提供有关调查内容的提示。


2个问题的解决方案:感谢@Richard Matsen!

a) 问题是,Google Maps API 的调用不在 Angular 区域内,因此不会自动触发更改检测。将 API 调用包装在 ngZone.run() 函数中的服务中起到了作用:

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

  );

b) 使用主题不会在每次新击键时都导致新流解决了异步管道无法正常工作的问题,请参阅下面的注释以获取代码。


完整的组件、服务和模板如下:

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

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

<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>

【问题讨论】:

顺便说一句,您可能需要添加angular-google-maps 标签。 完成,希望它现在不会与 AngularJS 混淆。 【参考方案1】:

手动调用ChangeDetectorRef.detectChanges 修复了事件滞后。

我猜这个 api 调用超出了 Angular 的自动更改检测,所以每次新结果到达时都需要触发它。

place-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

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

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

【讨论】:

非常感谢您的回答!这解决了尝试使用 | 实现承诺版本时的问题。异步的。但是 changeDetection 仅在第二个事件上触发的问题仍然存在。我尝试将 this.placePredictionService.getPlacePredictions(term) 包装到 ngZone.run() 但没有帮助。 是的,你是对的。我的错,我认为这是由于网络滞后。 您有关于如何修复它的提示吗?包装在 ngZone.run() 没有帮助。 将 API 回调封装到 ngZone.run 语句中解决了这个问题。 大概效果一样,因为ngZone是变化检测的核心。

以上是关于Angular 5:无法使用异步管道更新模板的主要内容,如果未能解决你的问题,请参考以下文章

加载数据后设置反应形式(异步) - Angular 5

Angular 5:使用异步管道搜索 - 显示加载指示器

Angular 4模板“找不到管道”

找不到管道:Angular 5 自定义管道

Angular 订阅无法按我的预期工作

如何在生产版本上升级 Angular 7 后修复“错误:模板解析错误:找不到管道‘异步’”