如何从密钥列表创建ObservableList?

时间:2017-03-06 23:31:31

标签: angular firebase angularfire2

我正在尝试显示属于用户的所有“练习”。因为“运动”对象可以属于多个用户,所以这是典型的多对多关系。由于文档建议扁平化数据库的结构,因此每个用户都有一个{ $exercise_key: true }的字典。

经过几个小时的搜索,我终于得到了一些有用的东西,但它很丑陋,看起来非常缓慢而效率低下。我首先从用户那里获得一个可观察的运动键列表,然后对于每个键,我得到一个FirebaseObservableObject,我将其作为'元数据'属性附加。

当我加载页面时,首先我看到一个空子弹列表,然后只有一半的子弹得到他们的文本,然后最后得到其余的。整个过程需要约2秒钟才能显示出来。即使我动态添加新练习而不重新加载页面,也会发生这种“闪烁”。请在https://papa-bear-1f486.firebaseapp.com上自行试用(使用google身份验证)。

我的数据库如下所示:

{
  "exercises" : {
    "-KeVLI3UbGkRbifLffMa" : {
      "createdAt" : 1488748884471,
      "name" : "squat1"
    },
    "-KeVLISTWaXbFxS6yqfv" : {
      "createdAt" : 1488748886066,
      "name" : "squat2"
    },
  },
  "users" : {
    "JtLTkOb8EXTeCeasF6vLpIpoUDB2" : {
      "exercises" : {
        "-KeVLI3UbGkRbifLffMa" : true,
        "-KeVLISTWaXbFxS6yqfv" : true,
      }
    }
  }
}

这是我提出的丑陋代码:

private exercisesKey$: FirebaseListObservable<any[]>;
private exercises$: Observable<any[]>;

constructor(private af: AngularFire, private auth: AuthService) {
  const path = '/users/' + auth.id + '/exercises';

  this.exercisesKey$ = af.database.list(path);
  this.exercises$ = this.exercisesKey$.map((items) => {
    items.map(item => {
      item.metadata = this.getExercise(item.$key);
      return item;
    });
  return items;
});

getExercise(key: string): FirebaseObjectObservable<IExercise> {
    return this.af.database.object('/exercises/' + key);
}

在我看来:

<ul>
  <li *ngFor="let exercise of exerciseService.exercises$ | async">
    {{ (exercise.metadata | async)?.name }}
  </li>
</ul>

所以我想我的问题是:如何获得从密钥列表中创建的可观察对象列表?

的package.json:

"angularfire2": "^2.0.0-beta.5",
"firebase": "^3.5.2",

1 个答案:

答案 0 :(得分:1)

除了您想要执行检索每个练习的单个查询之外,您无法做任何事情来避免对每个练习进行查询。但是,您可以稍微简化一下。

您可以使用combineLatest撰写可观察的练习:

import 'rxjs/add/observable/combineLatest';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/switchMap';

constructor(private af: AngularFire, private auth: AuthService) {
  const path = '/users/' + auth.id + '/exercises';
  this.exercisesKey$ = af.database.list(path);
  this.exercises$ = this.exercisesKey$
    .switchMap((items) => items.length === 0 ?
      Observable.of([]) :
      Observable.combineLatest(...items.map(item => this.getExercise(item.$key)));
    );
});

如果运动键列表发生变化或运动本身发生变化,则组合的observable将重新发出练习列表。

如果您只想要练习的快照,并且不希望在练习发生变化时重新发出观察,请使用forkJoin代替combineLatest并使用first完成每项可观察的练习:

import 'rxjs/add/observable/forkJoin';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/first';
import 'rxjs/add/operator/switchMap';

constructor(private af: AngularFire, private auth: AuthService) {
  const path = '/users/' + auth.id + '/exercises';
  this.exercisesKey$ = af.database.list(path);
  this.exercises$ = this.exercisesKey$
    .switchMap((items) => items.length === 0 ?
      Observable.of([]) :
      Observable.forkJoin(...items.map(item => this.getExercise(item.$key).first()));
    );
});

在这两种情况下,您的模板看起来都是这样的:

<ul>
  <li *ngFor="let exercise of exerciseService.exercises$ | async">
    {{ exercise.name }}
  </li>
</ul>