如何在 Angular2 中触发变更检测? [复制]

Posted

技术标签:

【中文标题】如何在 Angular2 中触发变更检测? [复制]【英文标题】:How to trigger change detection in Angular2? [duplicate] 【发布时间】:2016-06-01 21:28:20 【问题描述】:

我正在创建一个调用 Facebook javascript api 的 Facebook 服务,并且想知道如何在我的值更新时最好地实现更改检测。

我有一个 UserService,它有一个 currentUser 属性,它是一个 BehaviorSubject:

currentUser: Subject<User> = new BehaviorSubject<User>(new User(null));

当我想更新用户以响应 facebook javascript sdk 告诉我用户已登录或注销时,我会更新该用户并需要在 ApplicationRef 上调用 tick()

updateUser(user: User) 
  console.log('UserService.updateUser:', user);
  this.currentUser.next(user);
  this.appRef.tick(); // UI does not update without this


constructor(facebook: Facebook) 
  this.facebook.facebookEvents.filter(x => x != null 
         && x.eventName == 'auth.authResponseChange')
      .subscribe((event) => 
        this.updateUser(new User(event.data));
      

在我的组件中,我将来自用户服务的“currentUser”存储在构造函数中并绑定到 value 属性:

<h2>Logged into Facebook as currentUser.value.name</h2>
<p>Is this you? &nbsp; <img src="currentUser.value.profilePicUrl"></p>

我做错了吗?有没有比在外部库触发更改后必须调用 ApplicationRef.tick() 更好的方法?

编辑

我尝试使用 NgZone,但它不起作用,使用不同的事件将提要中的帖子作为服务页面返回:

constructor(userService: UserService, private ref: ApplicationRef, private zone: NgZone)

...

this.postsSubject.subscribe((post) => 
  this.zone.runOutsideAngular(() =>  // doesn't do anything
    this.posts.push(post);
    console.log('postsSubject POST, count is ', this.posts.length);
    ref.tick(); // required to update bindings
  );

控制台显示计数递增,但仅当我添加 ref.tick() 调用时,html 绑定 posts.length 才会更新...

我想我在某处看到您可以从***应用程序组件中为任何组件提供“输入”,这可能是登录用户的方式,但不是其他调用,例如在提要中获取帖子。 ..

【问题讨论】:

我不知道,但如果这对你有帮助,那就好了,***.com/questions/35500890/… currentUser 是一个 BehaviorSubject。您必须使用异步管道 (currentUser | async)?.value?.name 订阅它。 【参考方案1】:

我做错了吗?有没有比在外部库触发更改后必须调用 ApplicationRef.tick() 更好的方法?

这取决于。如果您在 Angular 之外调用 Facebook API(即在 Angular 之外注册异步事件处理程序),那么您需要手动运行更改检测作为处理任何事件的一部分。你可以

注入NgZone,然后在该对象上调用run(),这将在Angular区域内执行传递(回调)函数,然后自动调用ApplicationRef().tick(),这将对整个应用程序运行更改检测,或者 自己注入ApplicationRef并调用tick(),或者 自己注入 ChangeDetectorRef 并调用 detectChanges() -- 这只会在一个组件及其子组件上运行更改检测

注意,如果你注入NgZone,你就不想使用runOutsideAngular()。这将在 Angular 区域之外执行传递的(回调)函数,并且不会调用 ApplicationRef().tick(),因此不会执行更改检测。

如果您在 Angular 中调用 Facebook API,您可能能够避免上述所有情况,因为 Angular(通过使用 Zone.js)应该对异步事件调用进行猴子补丁。然后,当您的事件触发时,ApplicationRef.tick() 将自动被调用。有关此方法的更多讨论,请参阅https://***.com/a/34593821/215945。

【讨论】:

因此,避免整个应用程序更改检测的最佳方法可能是对组件使用 ChangeDetectorRef 并订阅用户更改事件并从那里调用它? @JasonGoemaat,如果只有一个组件的视图需要更新,那么这可能是最有效的方法。 (请注意,使用detectChanges() 检测到组件及其子项已更改。我将更新我的答案。)【参考方案2】:

我不确定它是否更好。我还能想到其他几种方法。

使用 NgZone

您可以注入 NgZone 并使用回调执行 run 方法:

 NgZone.run(()=>this.currentUser.next(user));

使用 setTimeout

setTimeout 会自动触发变化检测:

setTimeout(()=>this.currentUser.next(user));

【讨论】:

正确的是 ngZone,我们在 GoogleMaps 在地图上放置图钉时遇到了类似的问题。 你不能使用NgZone.run,必须注入它的实例。 而且使用setTimeout是不对的,它只会触发NgZone实例的outterZone(aka Zone.current)。所以它不会触发角度的变化检测。 @e-cloud,你确定吗? setTimeout 肯定会在 Angular 区域 (forkInnerZoneWithAngularBehavior) 内运行,并且在离开时会耗尽微/宏任务并触发更改检测。【参考方案3】:

您能否告诉我们您初始化Facebook 提供程序以及您在组件中的服务的方式?

选项#1

你的行为让我改变了 Facebook 代码在 Angular 区域之外运行,因此当事件触发时,Angular 更改检测不会运行。你

您也可以手动运行更改检测:

import  Component, ChangeDetectorRef  from 'angular2/core';

@Component(
  (...)
)
export class MyComponent 
  constructor(private cdr:ChangeDetectorRef) 
  

  someMethod() 
    this.cdr.detectChanges();
  

查看这些问题:

"async" pipe not rendering the stream updates How to force a component's re-rendering in Angular 2?

选项 #2

也许您的事件在组件注册currentUser 主题的回调之前被触发。在这种情况下,您可以稍后触发事件...

【讨论】:

以上是关于如何在 Angular2 中触发变更检测? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

我在 Angular 变更检测中的断点未在 checkAndUpdateView() 上触发

绑定到函数时如何触发角度变化检测?

Angular 2,Eventemitter 会触发变化检测吗?

如何在 Angular2 中触发表单验证器

#私藏项目实操分享# Angular Change Detection 的学习笔记

Angular2 InputFormControl 和 ValueChange 连续触发