Reputation: 89
I'm trying to loop through a Firebase List (using AngularFire2), and I want to get a Promise to indicate when the nested loops completed so I can process on the second action. I'm using the following code:
console.log('START');
this.af.database.list('Sites/123123/Users')
.map(users => {
users.map(user => {
console.log('found a user ', user);
this.af.database.list('registeredUsers/' + user.$key + '/Sites')
.forEach(sites => {
sites.forEach(site => {
if (site.$value == '123123') {
console.log('found site ', site.$key);
}
});
});
});
}).first().toPromise().then(function (x) {
console.log('ALL DONE')
});
The output is:
But I'm getting the Promise only after the outer loop, without waiting for the nested loop to complete. Any suggestions?
Upvotes: 0
Views: 1169
Reputation: 89
@Kalle
i tried this way too, also same :
console.log('START');
var siteUsers = this.af.database.list('Sites/-KcF5J9SoSHDrUEYO-Ed/Users');
const siteUsersObservable = Observable.from(siteUsers);
siteUsersObservable
.map(users => {
console.log('users = ', users);
return users.map(user => {
console.log('found a user ', user);
var userSites = this.af.database.list('registeredUsers/' + user.$key + '/Sites');
const userSitesObservable = Observable.from(userSites);
return userSitesObservable
.forEach(sites => {
console.log('sites = ', sites)
sites.forEach(site => {
if (site.$value == '-KcF5J9SoSHDrUEYO-Ed') {
console.log('found site ', site.$key);
}
});
});
});
})
.first().toPromise().then(x => {
console.log('ALL DONE')
});
and the firebase structure is like this:
and my result is :
Upvotes: 1
Reputation: 3907
You are missing return statements in the map calls, because you are generating new Observables inside the first map on the users observable you should use a mergeMap here:
console.log('START');
this.af.database
.list('Sites/123123/Users')
.mergeMap(users => {
return users.map(user => {
console.log('found a user ', user);
return this.af.database.list('registeredUsers/' + user.$key + '/Sites')
.forEach(sites => {
sites.forEach(site => {
if (site.$value == '123123') {
console.log('found site ', site.$key);
}
});
});
});
})
.first().toPromise().then(x => {
console.log('ALL DONE')
});
Here I have a simplified version of your code, eliminating dependencies to your firebase app:
//const Rx = require('rxjs'); // require Rx on nodejs instead of html script tag
const usersObservable = Rx.Observable.from([[{ id: 1, $key: 123 }]]);
const sitesObservable = Rx.Observable.from(
new Promise((resolve) => {
setTimeout(() => {
resolve([{ $value: '123123', $key: 'site1' }, { $value: '0', $key: 'site2' }])
}, 500);
})
);
console.log('START');
usersObservable
.mergeMap((users) => {
return users.map(user => {
console.log('found a user ', user);
return sitesObservable
.forEach(sites => {
sites.forEach(site => {
if (site.$value == '123123') {
console.log('found site ', site.$key);
}
});
});
});
})
.first().toPromise().then(x => {
console.log('ALL DONE')
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.js"></script>
Here is the generated output:
START
found a user {
"id": 1,
"$key": 123
}
found site site1
ALL DONE
With your specific firebase code added your Code should look like this:
console.log('START');
var siteUsers = this.af.database.list('Sites/-KcF5J9SoSHDrUEYO-Ed/Users');
siteUsers
.mergeMap(users => {
console.log('users = ', users);
return users.map(user => {
console.log('found a user ', user);
return this.af.database.list('registeredUsers/' + user.$key + '/Sites')
.forEach(sites => {
console.log('sites = ', sites)
sites.forEach(site => {
if (site.$value == '-KcF5J9SoSHDrUEYO-Ed') {
console.log('found site ', site.$key);
}
});
});
});
})
.first().toPromise().then(x => {
console.log('ALL DONE')
});
Upvotes: 1