web-dev-qa-db-ja.com

Angular2で変更検出をトリガーする方法は?

Facebook javascript apiを呼び出すFacebookサービスを作成していますが、値が更新されたときに変更検出を最適に実装する方法を考えています。

BehaviorSubjectであるUserServiceプロパティを持つcurrentUserがあります:

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

そして、facebook javascript sdkがユーザーにログインまたはログアウトしたことを通知するのに応じてユーザーを更新したい場合、それを更新し、ApplicationReftick()を呼び出す必要があります。

_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()呼び出しを追加した場合にのみ更新されます...

私はどこかであなたがトップレベルのアプリコンポーネントから任意のコンポーネントへの「入力」を利用できるようにしたと思います。これはログインユーザーのために行く方法かもしれませんが、フィードで投稿を取得するような他の呼び出しではありません...

14
Jason Goemaat

私は何か間違っていますか?外部ライブラリからトリガーされた変更の後にApplicationRef.tick()を呼び出すよりも良い方法はありますか?

場合によります。 Angularの外でFacebook APIを呼び出す場合(つまり、Angularの外で非同期イベントハンドラーを登録する場合)、イベント処理の一部として変更検出を手動で実行する必要があります。どちらか

  • NgZoneを注入し、そのオブジェクトに対してrun()を呼び出します。これにより、Angular=ゾーン内で渡された(コールバック)関数が実行され、ApplicationRef().tick()、アプリ全体の変更検出を実行するか、または
  • ApplicationRefを注入し、tick()を自分で呼び出す、または
  • ChangeDetectorRefを注入し、detectChanges()を自分で呼び出します。これにより、1つのコンポーネントとその子に対してのみ変更検出が実行されます。

NgZoneを注入する場合、runOutsideAngular()を使用したくないことに注意してください。これは、渡された(コールバック)関数をAngularゾーン)の外側で実行し、ApplicationRef().tick()を呼び出さないため、変更検出は実行されません。

Angular内でFacebook APIを呼び出すと、上記のすべてを回避できる可能性があります。なぜなら、Angular(Zone.jsの使用を介して)非同期イベント呼び出しをモンキーパッチする必要があるからです。その後、イベントが発生すると、ApplicationRef.tick()が自動的に呼び出されます。このアプローチの詳細については、 https://stackoverflow.com/a/34593821/215945 を参照してください。

18
Mark Rajcok

それが良いかどうかはわかりません。他にも考えられる方法がいくつかあります。

NgZoneを使用する

NgZoneを注入し、コールバックを使用してrunメソッドを実行できます。

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

SetTimeoutを使用する

setTimeoutは変更検出を自動的にトリガーします:

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

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サブジェクトにコールバックを登録する前に、おそらくイベントが発生します。この場合、後でイベントをトリガーできます...

5