Extracting multiple observables into an already mapped stream angular rxjs - javascript

Currently building a project in Firebase and Angular 4. I am pulling in an object of data which contains many keys of data, and a lot of different uid keys.
To keep the data somewhat manageable i don't want to save the object data inside some keys where it can be changed at the host.
In the instance i have the uid stored, i want to create a new key of the extracted observable. I can easily add it as an observable inside the mapped object but i want to actually extract the observable into the object during the stream. Here is what i currently have:
this.ss.data()
.map(res => {
res['test1'] = this.fb.serie(res['serieUid']).map(res => res)
return res
})
.subscribe(res => {
console.log(res)
})
Result -
serieUid:"-Kk1FeElvKMFcyrhQP4T"
state:"Tennessee"
test1:FirebaseObjectObservable
timezone:"US/Eastern"
But i would like to have the extracted observable inside 'test1', I have failed on this for a few hours on this and am confused. There is more than one instance of a uid, so this was have to happen multiple times. Then subscribe.
Follow up after answered below:
Heres what i ended up with in my final function
getRound(roundUid: string) {
/**
Retrieves a round based simply on the uid
supplied.
**/
return this.fb.round(roundUid)
.mergeMap(round => // Get Serie Data
this.fb.serie(round['serieUid']).map(serie => {round['serie'] = this.cs.cleanForFb(serie); return round})
)
.mergeMap(round => // Get Type Data
this.fb.type(round['typeUid']).map(type => {round['type'] = this.cs.cleanForFb(type); return round})
)
.mergeMap(round => // Get sx Coast Data
this.fb.coast(round['sxCoastUid']).map(sxCoast => {round['sxCoast'] = this.cs.cleanForFb(sxCoast); return round})
)
}

Try this. The mergeMap() operator subscribes to the internal Observable whose result is using map() turned into the original res updated with test1.
this.ss.data()
.mergeMap(res => this.fb.serie(res['serieUid'])
.map(innerRes => {
res['test1'] = innerRes;
return res;
})
)
.subscribe(res => {
console.log(res)
})

Related

Why changes made in database not updates in realtime?

I am trying to learn firestore realtime functionality.
Here is my code where I fetch the data:
useEffect(() => {
let temp = [];
db.collection("users")
.doc(userId)
.onSnapshot((docs) => {
for (let t in docs.data().contacts) {
temp.push(docs.data().contacts[t]);
}
setContactArr(temp);
});
}, []);
Here is my database structure:
When I change the data in the database I am unable to see the change in realtime. I have to refresh the window to see the change.
Please guide me on what I am doing wrong.
Few issues with your useEffect hook:
You declared the temp array in the way that the array reference is persistent, setting data with setter function from useState requires the reference to be new in order to detect changes. So your temp array is updated (in a wrong way btw, you need to cleanup it due to now it will have duplicates) but React is not detectign changes due to the reference to array is not changed.
You are missing userId in the dependency array of useEffect. If userId is changed - you will continue getting the values for old userId.
onSnapshot returns the unsubscribe method, you have to call it on component unMount (or on deps array change) in order to stop this onSnapshot, or it will continue to work and it will be a leak.
useEffect(() => {
// no need to continue if userId is undefined or null
// (or '0' but i guess it is a string in your case)
if (!userId) return;
const unsub = db
.collection("users")
.doc(userId)
.onSnapshot((docs) => {
const newItems = Object.entries(
docs.data().contacts
).map(([key, values]) => ({ id: key, ...values }));
setContactArr(newItems);
});
// cleanup function
return () => {
unsub(); // unsubscribe
setContactArr([]); // clear contacts data (in case userId changed)
};
}, [userId]); // added userId

React Native API FETCH Different names for each objects

I am connecting a REST api from React Native app. I have Json response with filename objects with different names but all the objects have same variables: filename, message, and display.
Number of objects changes with each request to API (REST), the names of objects in response are different depending on requests. But the variables in each object are same as above.
The information I need from this response is only filename text, but it will be acceptable if I get list of objects so I can read through the messages from errors.
The image shows how my objects look like.
This is my fetch request :
const getGists = async () => {
await axios
.get(`https://api.github.com/gists/public?per_page=30`)
.then((r) => {
let n;
for (n = 0; n < 30; n++) {
console.log(r.data[n].files.filename);
// console.log("____________________");
// console.log(r.data[n].owner.avatar_url);
// console.log("____________________");
// console.log(JSON.stringify(r.data[n].files));
}
})
.catch((e) => {
console.log("ERROR", e);
});
};
how is possible to get every filename from these requests even if object name is not the same in each iteration . Thanks for help
Working with the result of the API calls and some higher-order functions, this will work fine:
const getGists = async () => {
await axios
.get(`https://api.github.com/gists/public?per_page=30`)
.then((response) => {
const myDesireResult = response.data.reduce((acc, item) => {
const files = Object.values(item.files);
if (files.length > 1) {
files.forEach((file) => acc.push(file.filename));
} else {
acc.push(files[0].filename);
}
return acc;
}, []);
console.log(myDesireResult);
})
.catch((e) => {
console.log("ERROR", e);
});
};
Explanation:
in the then block, can get the API call result with result.data
with reduce function, looping through the data will start.
since the object in the files has different names, we can get the files with Object.values() easily.
Some of the files contain several items and most of them have just one item. so with checking the length of the file we can do proper action. if the files have more than one element, with another simple lop, we can traverse this file array easily.
Check the working example on codesandbox

Firestore statechanges() Angular doesnt return anything when there is no data

I am querying a firestore collection with some where(). After applying the filter there is no data match in the collection. In this situation the firestore doesnt return anything. I have a loading icon which I need to disable if there is no data returned on filtering.
I am using ngrx statechanges to query the data
ofType(quoteActions.queryQuotes),
withLatestFrom(this.salesFacade.getFilter$),
switchMap(([action, filter]) => {
let response = this.dfs.collection<IQuotesDataModel>('quotes', (ref) => { return
ref2.orderBy('createdDate', 'desc').limit(50);
}).stateChanges()
return response;
}),
mergeMap(actions => actions),```

Angularfire - How to apply 'where' conditions with snapshotChanges()

I am rather new to angular & firebase and current using the angular/fire to retrieve the collections with the condition as per below in the service;
service.ts
get posts() {
return this.afs
.collection<Todo>('posts', ref => {
let query:
| firebase.firestore.CollectionReference
| firebase.firestore.Query = ref;
query = query.where('posted', '==', true);
return query;
})
.snapshotChanges()
.pipe(
map(action =>
action.map(a => {
const data = a.payload.doc.data();
const id = a.payload.doc.id;
return { id, ...data };
}),
),
);
}
In the component I am subscribing to the observable to retrieve the posts with "posted" field equals true. The below is working as expected and only the "posted" posts are being logged.
component.ts
ngOnInit() {
this.service.posts.subscribe(posts => {
console.log(posts);
})
}
But, when adding the post with the "posted" field equals to false, this post is also being logged in the console.
Partially it makes sense to me, since the collection is updated and the component is subscribed to it, resulting in an update.
However, my expectations was event if the collections has changed, it will still have the conditions applied & filter out again.
Thank you all for helping out a newbie
I don't know angularfire2 and firebase too well but you can take advantage of the map operator and filter operator of array maybe.
Try:
ngOnInit() {
this.service.posts.pipe(
map(posts => posts.filter(post => post.posted)),
).subscribe(posts => {
console.log(posts);
})
}

How to get the results of a query as an Object from Cloud Firestore?

I migrated from firebase Realtime Database to Cloud firestore in one of my ReactJS projects. And I am fairly new to Firestore.
Here's what I need. Consider that I have details of items stored in a collection 'Items'.
In Realtime database, I just had to do this to get the data and store it in the state:
database.ref('Items').once('value', (snapshot) => {
this.setState({items: snapshot.val()})
})
snapshot.val() returns all the objects in Items as a single object containing them. That makes it easy for me to render them in a table with Object.keys(this.state.items).map()
From what I know so far:
This is not the case with Firestore. Firestore returns an object that can only retrieve the data in the child only by iterating through each one of them.
So the best I could come up with was:
db.collection('Items').get().then(gotData)
gotData = (snapshot) => {
snapshot.forEach((item) => {
const item = {[item.id]: item.data()}
this.setState(previousState => ({
items : {...previousState.items, ...item}
}))
})
}
The problem with the above solution is that the state will get updated a number of times equal to the number of items in the collection. So I wonder if there is a better way to do it.
Almost there, you just want to put the empty object outside the iteration like so...
db.collection('Items').get().then(snap => {
const items = {}
snap.forEach(item => {
items[item.id] = item.data()
})
this.setState({items})
}
Or you could just map it, which I find easier to think about.
db
.collection('Items')
.get()
.then(collection => {
const items = collection.docs.map(item => {[item.id]: item.data()})
this.setState({ items })
})
Or you can use reduce to return an object containing all the items.
db
.collection('Items')
.get()
.then((collection) => {
const items = collection.docs.reduce((res, item) => ({...res, [item.id]: item.data()}), {});
this.setState({items})
})

Categories

Resources