【问题标题】:Angular (2+) - getting a delay in data binding interpolationAngular (2+) - 在数据绑定插值中得到延迟
【发布时间】:2018-05-24 19:06:03
【问题描述】:

我正在使用 google 的位置建议 API 构建位置搜索输入。一切正常,结果几乎立即返回,但我在显示数据时遇到了几个问题。

  • 建议从服务返回,但有时可能需要几秒钟才能显示在视图中(您可以看到延迟,因为结果会立即显示在控制台中,但需要一些时间才能在视图中更改)
  • 有时视图中显示的结果会显示最后一组结果,而不是当前结果

我已经编写了我在 StackBlitz 上尝试做的事情的基本副本,https://google-location-suggestions.stackblitz.io,但在这里仍然遇到同样的问题。我已对其进行了更新,使其具有更好地显示问题的加载状态。

StackBlitz 与编辑器的链接: https://stackblitz.com/edit/google-location-suggestions

我还在此处添加了一个视频,以便您了解我的意思: https://vimeo.com/271476965

如果有人知道为什么会发生这种情况,我将不胜感激。

【问题讨论】:

  • 你可能忘记保存堆栈闪电战了。
  • @ibenjelloun 是的,我做到了,谢谢 - 刚刚保存了它。

标签: angular google-maps data-binding autocomplete interpolation


【解决方案1】:

“延迟”与变化检测有关,如果你在输入位置搜索后点击文档,angular会获取事件并刷新DOM。

地图 api 在角度加载后下载,这使得地图代码在 ngZone 之外运行。我在阅读this post 时理解了这一点。

主要解决方法(正如您之前在回答中提到的)是使用 ngZone.run 在 Angular Zone 中运行代码:

private suggestions$ = new BehaviorSubject([]);

public search(location: string): void {
    this.service.getPlacePredictions(
      {
        input: location,
        componentRestrictions: { country: 'uk' }
      },
      (predictions, status) => this._ngZone.run(() => this.suggestions$.next(predictions.map(p => p.description)))
    );
  }

我还添加了一些其他改进:

在构造函数中只加载一次maps api以避免已经加载的错误(stackblitz aot也会导致多次加载服务,所以我将重新加载机制更改为左下菜单的页面):

constructor(private mapsAPILoader: MapsAPILoader) {
    this.mapsAPILoader.load().then(() => {
      this.service = new google.maps.places.AutocompleteService();
      console.log('Maps API loaded');
      this.mapsApiLoading$.next(false);
    });
  }

您可以使用 rxjs 管道创建所需的 observable:

this.locationSuggestions$ = this.locationForm.get('location').valueChanges.pipe(
        filter(location => location && location !== ''),
        switchMap(location =>
            this.appService.getLocationSuggestions(location)));

这样你就可以在模板中使用async直接订阅你的observable:

<ng-container *ngIf="locationSuggestions$ | async as suggestions; else loading">
{{ suggestions | json }}
</ng-container>
<ng-template #loading>
  loading...
</ng-template>

有时,如果我足够快,我可以在加载地图 api 之前进行搜索,所以我添加了一个 loading observable,让组件知道他什么时候可以开始搜索。

Here is the new stackblitz with the changes.

【讨论】:

  • 谢谢,我认为这可能与变更检测有关。不幸的是,尽管您的解决方案在我刚尝试时似乎做了同样的事情。
  • 谢谢,我很欣赏更新的编辑,但问题仍然出现在你的代码中?如果您在位置输入中键入“na”,它会将“诺丁汉”列为第一个结果,而它应该是“自然历史博物馆...”,如果您按照您的建议单击输入,它会更新。
  • 对不起,我没有意识到,我完全错了^_^)"。这里离开ngZone的主要原因是maps api,这意味着别无选择,只能使用一些使用这些函数在 ngZone 中运行代码或运行更改检测。这篇文章帮助我理解了:stackoverflow.com/a/42000577/8945135。我用你的解决方案修复了我的 stackblitz,我正在修复我的答案。
  • 谢谢,我会读一读。还会将您的答案标记为正确,因为您将 ngZone 包含在服务中,这是它的最佳位置,而不是组件。
【解决方案2】:

@ibenjelloun(谢谢)建议的问题是变更检测。

我实现了 Angular 的 NgZone 来运行更改检测,以便通过将代码包装在 ngZone.run 方法中来更新绑定

this.ngZone.run(() => {
    this.locationSuggestionsLoading = false;
    this.locationSuggestions = locations;
});

【讨论】:

  • 使用ngZone.run()ChangeDetectorRef.markForCheck() 不是处理此类问题的最佳方式,它会使代码变得复杂。通常,当我需要使用其中之一时,我会回过头来思考如何改进我的组件/服务设计,使其在不使用它们的情况下工作。我对我的答案进行了编辑,并提供了改进的解决方案。
猜你喜欢
  • 1970-01-01
  • 2014-05-24
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 2016-08-08
  • 2018-04-17
  • 2019-04-06
  • 1970-01-01
相关资源
最近更新 更多