In my application I have the following firebase posts structure:
posts: {
text: \"..\"
meta: {..}
user: {
id: \"user1\",
username
When looking into this question, I discovered an issue with the FirebaseListObservable
implementation when preserveSnapshot
was false
- the default behaviour. Each time the observable emitted an array of unwrapped snapshots, the array elements were different instances - even if the elements themselves had not changed. And this resulted in much less efficient change detection.
A fix has been merged and will be included in the next release of AngularFire2 (that is, the release that follows 2.0.0-beta.7
) - which should be soon.
With the fix, if Angular's OnPush
change detection is used, AngularFire2's FirebaseListObservable
is very fast and efficient regarding DOM updates.
I usually split the components into container and presentational components, with the container component holding the observable and with the presentational component using OnPush
change detection.
The container component would look something like this:
import { Component } from "@angular/core";
import { AngularFire, FirebaseListObservable } from "angularfire2";
@Component({
selector: "items-container",
template: `<items-component [items]="items | async"></items-component>`
})
export class ItemsContainer {
private items: FirebaseListObservable<any>;
constructor(private angularFire: AngularFire) {
this.items = angularFire.database.list("some/fast-changing/data", {
query: {
limitToLast: 1000
}
});
}
}
And the presentational component would look something like this:
import { ChangeDetectionStrategy, Component, Input } from "@angular/core";
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "items-component",
template: `
<ul>
<li *ngFor="let item of items">{{ item | json }}</li>
</ul>
`
})
export class ItemsComponent {
@Input() items: any[];
}
With this arrangement of components, Angular's OnPush
change detection will ensure that only the elements that have actually changed will effect DOM updates. So even long, scrolling lists will be efficient and fast.
Another tip: a FirebaseListObserable
that uses a limit query will sometimes emit two values when the underlying data changes. For example, when a new item is added to the database, a list that queries the 10 most recent items (using limitToLast: 10
) will emit an array with the least recent item removed and will then immediately emit another with the most recent item added. If you don't want the first of those two arrays to be emitted, you can use the auditTime operator:
import "rxjs/add/operator/auditTime";
this.items = angularFire.database.list("some/fast-changing/data", {
query: {
limitToLast: 10
}
})
.auditTime(0);