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 & 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:无法使用异步管道更新模板的主要内容,如果未能解决你的问题,请参考以下文章