|
| 1 | +import { NgZone } from '@angular/core'; |
1 | 2 | import * as database from '@firebase/database-types';
|
2 |
| -import { ZoneScheduler } from 'angularfire2'; |
| 3 | +import { FirebaseZoneScheduler } from 'angularfire2'; |
3 | 4 | import * as utils from './utils';
|
4 | 5 | import { AFUnwrappedDataSnapshot } from './interfaces';
|
5 | 6 | import { FirebaseListObservable } from './firebase_list_observable';
|
@@ -109,84 +110,87 @@ export function FirebaseListFactory (
|
109 | 110 | */
|
110 | 111 | function firebaseListObservable(ref: database.Reference | DatabaseQuery, {preserveSnapshot}: FirebaseListFactoryOpts = {}): FirebaseListObservable<any> {
|
111 | 112 |
|
112 |
| - const toValue = preserveSnapshot ? (snapshot => snapshot) : utils.unwrapMapFn; |
113 |
| - const toKey = preserveSnapshot ? (value => value.key) : (value => value.$key); |
114 |
| - |
115 |
| - const listObs = new FirebaseListObservable(ref, (obs: Observer<any>) => { |
116 |
| - |
117 |
| - // Keep track of callback handles for calling ref.off(event, handle) |
118 |
| - const handles: { event: string, handle: (a: DatabaseSnapshot, b?: string | null | undefined) => any }[] = []; |
119 |
| - let hasLoaded = false; |
120 |
| - let lastLoadedKey: string = null!; |
121 |
| - let array: DatabaseSnapshot[] = []; |
122 |
| - |
123 |
| - // The list children are always added to, removed from and changed within |
124 |
| - // the array using the child_added/removed/changed events. The value event |
125 |
| - // is only used to determine when the initial load is complete. |
| 113 | + const zone = new NgZone({}); |
| 114 | + return zone.runOutsideAngular(() => { |
| 115 | + const toValue = preserveSnapshot ? (snapshot => snapshot) : utils.unwrapMapFn; |
| 116 | + const toKey = preserveSnapshot ? (value => value.key) : (value => value.$key); |
| 117 | + |
| 118 | + const listObs = new FirebaseListObservable(ref, (obs: Observer<any>) => { |
| 119 | + |
| 120 | + // Keep track of callback handles for calling ref.off(event, handle) |
| 121 | + const handles: { event: string, handle: (a: DatabaseSnapshot, b?: string | null | undefined) => any }[] = []; |
| 122 | + let hasLoaded = false; |
| 123 | + let lastLoadedKey: string = null!; |
| 124 | + let array: DatabaseSnapshot[] = []; |
| 125 | + |
| 126 | + // The list children are always added to, removed from and changed within |
| 127 | + // the array using the child_added/removed/changed events. The value event |
| 128 | + // is only used to determine when the initial load is complete. |
| 129 | + |
| 130 | + ref.once('value', (snap: any) => { |
| 131 | + if (snap.exists()) { |
| 132 | + snap.forEach((child: any) => { |
| 133 | + lastLoadedKey = child.key; |
| 134 | + }); |
| 135 | + if (array.find((child: any) => toKey(child) === lastLoadedKey)) { |
| 136 | + hasLoaded = true; |
| 137 | + obs.next(array); |
| 138 | + } |
| 139 | + } else { |
| 140 | + hasLoaded = true; |
| 141 | + obs.next(array); |
| 142 | + } |
| 143 | + }, err => { |
| 144 | + if (err) { obs.error(err); obs.complete(); } |
| 145 | + }); |
126 | 146 |
|
127 |
| - ref.once('value', (snap: any) => { |
128 |
| - if (snap.exists()) { |
129 |
| - snap.forEach((child: any) => { |
130 |
| - lastLoadedKey = child.key; |
131 |
| - }); |
132 |
| - if (array.find((child: any) => toKey(child) === lastLoadedKey)) { |
| 147 | + const addFn = ref.on('child_added', (child: any, prevKey: string) => { |
| 148 | + array = onChildAdded(array, toValue(child), toKey, prevKey); |
| 149 | + if (hasLoaded) { |
| 150 | + obs.next(array); |
| 151 | + } else if (child.key === lastLoadedKey) { |
133 | 152 | hasLoaded = true;
|
134 | 153 | obs.next(array);
|
135 | 154 | }
|
136 |
| - } else { |
137 |
| - hasLoaded = true; |
138 |
| - obs.next(array); |
139 |
| - } |
140 |
| - }, err => { |
141 |
| - if (err) { obs.error(err); obs.complete(); } |
142 |
| - }); |
| 155 | + }, err => { |
| 156 | + if (err) { obs.error(err); obs.complete(); } |
| 157 | + }); |
| 158 | + handles.push({ event: 'child_added', handle: addFn }); |
143 | 159 |
|
144 |
| - const addFn = ref.on('child_added', (child: any, prevKey: string) => { |
145 |
| - array = onChildAdded(array, toValue(child), toKey, prevKey); |
146 |
| - if (hasLoaded) { |
147 |
| - obs.next(array); |
148 |
| - } else if (child.key === lastLoadedKey) { |
149 |
| - hasLoaded = true; |
150 |
| - obs.next(array); |
151 |
| - } |
152 |
| - }, err => { |
153 |
| - if (err) { obs.error(err); obs.complete(); } |
154 |
| - }); |
155 |
| - handles.push({ event: 'child_added', handle: addFn }); |
| 160 | + let remFn = ref.on('child_removed', (child: any) => { |
| 161 | + array = onChildRemoved(array, toValue(child), toKey); |
| 162 | + if (hasLoaded) { |
| 163 | + obs.next(array); |
| 164 | + } |
| 165 | + }, err => { |
| 166 | + if (err) { obs.error(err); obs.complete(); } |
| 167 | + }); |
| 168 | + handles.push({ event: 'child_removed', handle: remFn }); |
156 | 169 |
|
157 |
| - let remFn = ref.on('child_removed', (child: any) => { |
158 |
| - array = onChildRemoved(array, toValue(child), toKey); |
159 |
| - if (hasLoaded) { |
160 |
| - obs.next(array); |
161 |
| - } |
162 |
| - }, err => { |
163 |
| - if (err) { obs.error(err); obs.complete(); } |
164 |
| - }); |
165 |
| - handles.push({ event: 'child_removed', handle: remFn }); |
| 170 | + let chgFn = ref.on('child_changed', (child: any, prevKey: string) => { |
| 171 | + array = onChildChanged(array, toValue(child), toKey, prevKey); |
| 172 | + if (hasLoaded) { |
| 173 | + obs.next(array); |
| 174 | + } |
| 175 | + }, err => { |
| 176 | + if (err) { obs.error(err); obs.complete(); } |
| 177 | + }); |
| 178 | + handles.push({ event: 'child_changed', handle: chgFn }); |
| 179 | + |
| 180 | + return () => { |
| 181 | + // Loop through callback handles and dispose of each event with handle |
| 182 | + // The Firebase SDK requires the reference, event name, and callback to |
| 183 | + // properly unsubscribe, otherwise it can affect other subscriptions. |
| 184 | + handles.forEach(item => { |
| 185 | + ref.off(item.event, item.handle); |
| 186 | + }); |
| 187 | + }; |
166 | 188 |
|
167 |
| - let chgFn = ref.on('child_changed', (child: any, prevKey: string) => { |
168 |
| - array = onChildChanged(array, toValue(child), toKey, prevKey); |
169 |
| - if (hasLoaded) { |
170 |
| - obs.next(array); |
171 |
| - } |
172 |
| - }, err => { |
173 |
| - if (err) { obs.error(err); obs.complete(); } |
174 | 189 | });
|
175 |
| - handles.push({ event: 'child_changed', handle: chgFn }); |
176 |
| - |
177 |
| - return () => { |
178 |
| - // Loop through callback handles and dispose of each event with handle |
179 |
| - // The Firebase SDK requires the reference, event name, and callback to |
180 |
| - // properly unsubscribe, otherwise it can affect other subscriptions. |
181 |
| - handles.forEach(item => { |
182 |
| - ref.off(item.event, item.handle); |
183 |
| - }); |
184 |
| - }; |
185 | 190 |
|
| 191 | + // TODO: should be in the subscription zone instead |
| 192 | + return observeOn.call(listObs, new FirebaseZoneScheduler(zone)); |
186 | 193 | });
|
187 |
| - |
188 |
| - // TODO: should be in the subscription zone instead |
189 |
| - return observeOn.call(listObs, new ZoneScheduler(Zone.current)); |
190 | 194 | }
|
191 | 195 |
|
192 | 196 | export function onChildAdded(arr:any[], child:any, toKey:(element:any)=>string, prevKey:string): any[] {
|
|
0 commit comments