17

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

我有UserService一个currentUser属性是 BehaviorSubject:

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

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

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
  });
}

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

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

4

3 回答 3

20

难道我做错了什么?有没有比在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://stackoverflow.com/a/34593821/215945

于 2016-02-20T18:32:31.577 回答
13

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

使用 NgZone

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

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

使用 setTimeout

setTimeout 将自动触发更改检测:

setTimeout(()=>this.currentUser.next(user));
于 2016-02-20T07:22:51.023 回答
5

你能告诉我们Facebook你在你的组件中初始化提供者和你的服务的方式吗?

选项1

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

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

import { Component, ChangeDetectorRef } from 'angular2/core';

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

  someMethod() {
    this.cdr.detectChanges();
  }
}

请参阅以下问题:

选项 #2

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

于 2016-02-20T08:17:07.427 回答