2017-02-17 25 views
5

我在應用程序內部進行了一些基本的變化檢測,我掙扎了很多。我已經在這裏呆了兩天,失去了意志!我想知道這裏有沒有人能指出我正確的方向。rxjs combine在Observable Arrays上檢測最新和Anguar2變化

我有一個group與2 user名單。一個是members,另一個是moderators

此方法連接到數據庫以獲取每個對象的列表,其值爲$key。現在我想使用這些鍵去獲取每個用戶的實際UserData。我嘗試了一切,combineLatest是我似乎能夠工作的唯一的東西。

所以這兩個調用函數,我在我的模板上使用async管道。

members$ = myService.getMemberListByType("blah", "members"); 
moderators$ = myService.getMemberListByType("blah", "moderators"); 

我的功能如下所示。

private getMemberListByType(groupKey: string, memberType: string) { 

    return this.af.database.list(`groups/${groupKey}/${memberType}`) 
     .switchMap(userListKeyMap => { 

     console.log("UserListKeyMap", memberType, userListKeyMap); 

     let usersObservables: Observable<UserData>[] = []; 
     userListKeyMap.forEach((object) => { 
      usersObservables.push(this.af.database.object(`users/${object.$key}`) 
      .map(UserData.fromJSON)); 
     }); 

     return Observable.combineLatest(usersObservables); 
     }); 

    } 

但是,Angular沒有檢測到對此列表的更改,如下所示。假設其他方法在其他地方刪除member並將其添加到moderator。在這種情況下,members列表正在發出一個空的用戶列表[]

My * ngIf和* ngFor不檢測這個新的空對象的變化。結果是用戶(在模板上)現在是2個列表的一部分,但是這個數據下的數據非常準確,如日誌所示。

我覺得當我在一個空的數組上組合最大值時,這是我的問題的根源。什麼都不能發出....所以角度如何改變?我不知道如何解決它。在這個「空白」用例中有沒有其他的方法可以組合使用?由於我使用的是async管道,我需要一些有意義的東西。

任何人都可以照亮我的問題?

謝謝!

更新

我相信這個問題是不是angular2檢測空觀察的名單。特別是當一個Observable數組有一個值時,但後來該值變空。 Angular2不會看到空數組。目前我無法解決這個問題。要麼我的方法是錯誤的,要麼就是需要註冊一個空的可觀察列表?

screen of problem

  1. 藍色=組件的加載狀態是好的時,用戶是一個成員。
  2. Green =用戶已從成員移到經理/主持人。
  3. 紅色=這不存在,爲什麼它仍然顯示?
+0

建議你周圍有這樣的回答HTTP看看://計算器.com/questions/41584409/change-detection-in-angular-2/41585545#41585545 – Aravind

+0

你在teamviewer中可用嗎? – Aravind

回答

4

有簡單的解決方法:

private getMemberListByType(groupKey: string, memberType: string) { 

    return this.af.database.list(`groups/${groupKey}/${memberType}`) 
     .switchMap(userListKeyMap => { 

     console.log("UserListKeyMap", memberType, userListKeyMap); 

     let usersObservables: Observable<UserData>[] = []; 
     userListKeyMap.forEach((object) => { 
      usersObservables.push(this.af.database.object(`users/${object.$key}`) 
      .map(UserData.fromJSON)); 
     }); 

     return usersObservables.length > 0 ? Observable.combineLatest(usersObservables) : Observable.of([]); 
     }); 
} 
+0

謝謝!它似乎工作!對於任何一個感到困惑的人,注意''combineLatest([])'有一個空數組是有效的,但永遠不會發射一個值,而'([])'確實會發射一個值。這是更改模板中的檢測的關鍵。我很欣賞Kemsky。 – Clark

+0

這是很棒的+1,謝謝! – dhndeveloper

2

你應該嘗試這樣的事:

private getMemberListByType(groupKey: string, memberType: string) { 

    return this.af.database.list(`groups/${groupKey}/${memberType}`) 

      // Force the source obs to complete to let .toArray() do its job. 
      .take(1) 

      // At this point, the obs emits a SINGLE array of ALL users. 
      .do(userList => console.log(userList)) 

      // This "flattens" the array of users and emits each user individually. 
      .mergeMap(val => val) 

      // At this point, the obs emits ONE user at a time. 
      .do(user => console.log(user)) 

      // Load the current user 
      .mergeMap(user => this.af.database.object(`users/${user.$key}`)) 

      // At this point, the obs emits ONE loaded user at a time. 
      .do(userData => console.log(userData)) 

      // Transform the raw user data into a User object. 
      .map(userData => User.fromJSON(userData)) 

      // Store all user objects into a SINGLE, final array. 
      .toArray(); 

} 

注意。以do()開頭的行只是爲了解釋發生了什麼。無需將它們保留在最終代碼中。

+0

非常感謝!我知道我的跛足嘗試必須成爲這個問題的核心。我很喜歡這樣的系列教程,而不是rxjs文檔。我現在遇到的唯一問題是我無法訂閱回覆。我看到你的跟蹤語句做了,但奇怪'this.groupService.getGroupManagerList(groupKey).subscribe(success => console.log(success))'不會觸發(或錯誤),這很奇怪。 – Clark

+0

確實很奇怪。如果您在控制檯中看到日誌,則意味着可執行文件被執行。你確定你正在訂閱正確的方法嗎?你的初始代碼提到'getMemberListByType()',現在你說'getGroupManagerList()'。 – AngularChef

+0

哦,是的,抱歉的混亂!我不直接訪問這個方法,但有一些其他的方法來返回它,所以我可以隱藏字符串類型。基本上它是一樣的。我的'async'管道對它沒有反應,也沒有我上面的訂閱。不是它錯誤,或者當我訂閱時,它是不確定的,再加上我看到你正在執行的'do's'我正在檢查爲什麼現在,因爲我以前從未見過。 – Clark

4

你的方法對我來說似乎是正確的。使用帶空數組的combineLatest()不應以錯誤結尾(類似於例如forkJoin,使用空數組是一個有效的用例)。然後,即使它完成,因爲你使用的空數組switchMap,所以這應該不是我想的問題。

我沒有與任何AngularFire2經驗,所以我不知道是否this.af.database.object只發出一次所有項目,然後完成它發出的每一個變化值(這可能是你想要什麼,如果我猜測)。

反正用combineLatest()forkJoin()一個非常普遍的問題是,它們的源觀測量總是要發射至少一個項目。換言之,如果this.af.database.object中的任何一個爲空,那麼combineLatest()將不會發射任何東西。

您可以確保至少有一個值爲startWith()。比如像下面這樣:

userListKeyMap.forEach((object) => { 
    usersObservables.push(this.af.database.object(`users/${object.$key}`) 
     .startWith('{}') 
     .map(UserData.fromJSON)); 
}); 

如果這沒有幫助請嘗試使用剛剛Observable.timer()代替this.af.database.objectthis.af.database.list確保Angular2訂閱它,即使保持新項目時所發出的訂閱。

+0

我在這裏重構了我的評論。謝謝!不幸的是,我無法得到你的任何建議,我也不想使用'.startWith'。雖然閱讀它很棒!就是這樣,那裏什麼也沒有,假裝似乎有多餘。 – Clark

1

嘗試觀測名單上使用forkJoin

let userListKeyMap$ = Rx.Observable.of([{key:1}, {key:2}, {key:3}]); //mock keys 
 
let requestUserFromKey = (key) => Rx.Observable.of({user: key}).delay(1000); //mock users 
 

 
let result$ = userListKeyMap$.switchMap((userKeys) => 
 
Rx.Observable.forkJoin(userKeys.map(uk => requestUserFromKey(uk.key)))); 
 

 
result$.subscribe((users) => console.log('got some users: ', users));
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>

+0

嘿卡羅拉謝謝!我能夠在相同的背景下對此進行按摩,但我認爲可觀察的創作不是問題。即使有你的建議,Angular2也不會檢測到空數組。 (請參閱上面的問題中的屏幕截圖)。 – Clark