React Promise Return Firebase Array - arrays

I am having a really hard time solving this issue I'm currently having. You see, I am using Firebase as my database to store and fetch data.
Right now, I want to be able to return an array that is being made inside a Firebase .once call, but I am having some difficulties. This is my code so far:
Calling the function (a return function):
<p>{this.fetchStartingPrice(singleProduct.id)}</p>
This is where I want to display the specific value, that I am trying to fetch down below:
fetchStartingPrice(category){
let promises = [];
promises.push(this.fetchPrices(category));
Promise.all(promises).then(result => {
console.log(result);
})
}
I have just used a console.log in an attempt to troubleshoot errors.
fetchPrices(category){
var allPrices = [];
allProductsFirebase.child(category).once('value', (snapshot) => {
snapshot.forEach((childSnapshot) => {
if(childSnapshot.key == category){
allPrices.append(childSnapshot.val().brand);
}
});
return allPrices;
})
}
So basically, I want to loop through the allProductsFirebase in an attempt to first identify the brand of the product, and if it matches with the brand that has been used as a parameter in fetchStartingPrice() and fetchPrises(), I want to store the specific price of that product in an array of numbers (prices). After I have looped through the whole snapshot, I want to return the full array containing only product prices, and then through fetchStartingPrice(), I want to use Math.min(promises) to grab the lowest number in that array. However, I am having a really hard time doing this. Could someone please help me with this?
I want to be able to then, after all of this, return the value in fetchStartingPrice().

fetchPrices() must return Promise or be Promise. you are returning nothing from fetchPrices() ( you are returning allPrices in the .once() scope ). try to return the result ( if it returns Promise ) that .once() returns.
fetchPrices(category){
var allPrices = [];
return allProductsFirebase.child(category).once('value', (snapshot) => {
snapshot.forEach((childSnapshot) => {
if(childSnapshot.key == category){
allPrices.append(childSnapshot.val().brand);
}
});
return allPrices;
})
}

Related

Velo by Wix, JSON data in repeater

I'm trying to get the temperature of each hour from this website: https://www.smhi.se/vader/prognoser/ortsprognoser/q/Stockholm/2673730
I'm getting the data from https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/16/lat/58/data.json. The "t" object is the temperature.
The problem I have is displaying the data for each hour in the repeater.
Here is my backend-code:
import { getJSON } from 'wix-fetch';
export async function getWeather() {
try {
const response = await getJSON('https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/16/lat/58/data.json');
console.log(response) // all data
const tempData = response.timeSeries[0].parameters[10].values[0];
return tempData // Only returns "t" - temperature
} catch (e) {
return e;
}
}
The backend part works, however the frontend doesn't.
import { getWeather } from 'backend/getSMHI.jsw'
$w.onReady(function () {
(
getWeather().then(weatherInfo => {
$w('#weatherRepeater').onItemReady(($item, itemData, index) => {
if (index > 6) {
$item('#tempText').text = itemData.timeSeries[index].parameters[1].values[0];
} else if (index === 6) {
$item('#tempText').text = itemData.timeSeries[index].parameters[0].values[0];
} else {
$item('#tempText').text = itemData.timeSeries[index].parameters[10].values[0];
} // The parameters number for "t" changes depending on the index
})
$w('#weatherRepeater').data = weatherInfo;
})
)
})
Seems like there are at least a couple of issues here.
First, you are retrieving a single number from the API and trying to put that in a repeater. From the description of what you're trying to do, it would seem that you mean to be retrieving a list of numbers, probably as an array. You probably want to do some filtering and/or mapping on the response data instead of directly accessing a single value.
Second, the data you send to a repeater must be in the proper format. Namely, it must be an array of objects, where each object has a unique _id property value (as a string). You are not doing that here. You are simply assigning it a number.
Third, and this is just an efficiency thing, you don't need to define the onItemReady inside the then(). Not that it will really make much of a difference here.

Unable to understand setState of react js function paramters in a specific call?

setListOfPosts(curPosts => {
let newPosts = [...curPosts];
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
}
});
//is curPosts an instance of array or complete array?? my listofPosts is an array of objects
Your setState call needs to return newPosts, and you're creating an array using the spread operator which is why it's coming back as an array of objects.
I'm not sure what your desired output is, but by adding a return function it will set the state:
setListOfPosts(curPosts => {
let newPosts = [...curPosts];
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
return newPosts
}
});
This is untested but if your logic is correct should return an array of objects with the objects alert value updated.
Another option would be to do your logic before your setState call, by creating a a newState array and then simply updating the state with that new array without the use of the callback.
The callback function is useful if you want to add a new object to state array or do something that preserves the initial state, in your example you could do it without the callback like this:
// Create a copy of the state array that you can manipulate
const newPosts = [...newPosts]
if (data.response) {
// Add your logic to the state copy
newPosts[newPosts.findIndex(p => p.id === postId)].alert = response.data;
// Replace state with state copy
setListOfPosts(newPosts)
}
Again untested but hopefully this should help you understand the use of the callback function and the right way to use it.

Angular/Firestore Collection Document Query to return a single document field from all documents into an array

I am performing a query on my collection documents and trying to return just all phone numbers into an array. I just want to set the phone numbers into array for use by another function. Firebase docs only show a console log for (doc.id) and (doc.data) and no practical use for any other objects in your documents. My console log for info.phoneNumbers returns all the phoneNumbers.
async getPhone() {
await this.afs.collection('members', ref => ref.where('phoneNumber', '>=', 0))
.get().toPromise()
.then(snapshot => {
if (snapshot.empty) {
console.log('No Matches');
return;
}
this.getInfo(snapshot.docs);
});
}
getInfo(data) {
data.forEach(doc => {
let info = doc.data();
console.log(info.phoneNumber, 'Phonenumbers');
// let myArray = [];
// myArray.push(doc.doc.data());
// const phoneNumber = info.phoneNumber as [];
// console.log(myArray, 'ARRAY');
return info.phoneNumber;
})
}```
Firestore is a "document store database". You fetch and store entire DOCUMENTS (think "JSON objects") at a time. One of the "anti-patterns" when using document store databases is thinking of them in SQL/relational DB terms. In SQL/relational DB, you "normalize" data. But in a document store database (a "NoSQL" database) we explicitly denormalize data -- that is, we duplicate data -- across documents on write operations. This way, when you fetch a document, it has all the data you need for its use cases. You typically want to avoid "JOINs" and limit the number of references/keys in your data model.
What you are showing in the code above is valid in terms of fetching documents, and extracting the phoneNumber field from each. However, use of .forEach() is likely not what you want. forEach() iterates over the given array and runs a function, but the return value of forEach() is undefined. So the return info.phoneNumber in your code is not actually doing anything.
You might instead use .map() where the return value of the map() function is a new array, containing one entry for each entry of the original array, and the value of that new array is the return value from map()'s callback parameter.
Also, mixing await and .then()/.catch() is usually not a good idea. It typically leads to unexpected outcomes. I try to use await and try/catch, and avoid .then()/.catch() as much as possible.
So I would go with something like:
try {
let querySnap = await this.afs.collection('members', ref =>
ref.where('phoneNumber', '>=', 0)).get();
let phoneNumbers = await this.getInfo(querySnap.docs[i].data());
} catch(ex) {
console.error(`EXCEPTION: ${ex.message}`);
}
getInfo(querySnapDocs) {
let arrayPhoneNumbers = querySnapDocs.map(docSnap => {
let info = doc.data();
let thePhoneNum = info.phoneNumber
console.log(`thePhoneNum is: ${thePhoneNum}`);
return thePhoneNum;
});
return arrayPhoneNumbers;
});
I solved this with help and I hope this may be helpful to others in Getting access to 1 particular field in your documents. In my service:
async getPhone() {
return await this.afs.collection('members', ref => ref.where('phoneNumber', '>=', 0))
.get().toPromise()
.then(snapshot => {
if (snapshot.empty) {
console.log('No Matches');
return;
}
return this.getInfoNum(snapshot.docs);
});
}
getInfoNum(data) {
return data.map(doc => {
let info = doc.data();
return info.phoneNumber
});
}
In my Component using typescript
phoneNumbers: string[] = [];
getPhone() {
this.dbService.getPhone().then(phoneNumbers => {
this.phoneNumbers = phoneNumbers;
this.smsGroupForm.controls.number.setValue(phoneNumbers.join(',')) //sets array seperated by commas
console.log(phoneNumbers);
});
}
This returns all the phone numbers in a comma separated array.
In my template I pull the numbers into an input for another function to send multiple text. Code in the template is not polished yet for the form, I am just getting it there for now.
<ion-list>
<ion-item *ngFor="let phoneNumber of phoneNumbers">
<ion-label position="floating">Phone Number</ion-label>
<ion-input inputmode="number"
placeholder="Phone Number"
formControlName="number"
type="number">{{ phoneNumber }}
</ion-input>
</ion-item>
</ion-list>

Not getting a return from React-redux

I want to filter the results from my store in React-Redux. Therefor I have created a filter selector.
My code only returns all the "shoppers", while it should only be returning the filtered shoppers.
Each shopper has an array of reviews that contains (one or more) objects with the review (text) and review number. We want to filter on the review number. See printscreen:
enter image description here
So to clarify, I want to filter by the reviews of a shopper and if that number >= the filter to then return only the shoppers that match this criteria.
What am I doing wrong here? How can I get it to return only the filtered results?
export const selectShoppersWithFilters = (filters) => (state) => {
let shoppers = [...state.publicShoppers];
if (filters.minAverageReview) {
return shoppers.filter((shopper) => {
return shopper.reviews.map((review) => {
if (review.review >= filters.minAverageReview) {
return (shoppers = shopper);
}
});
});
}
console.log(shoppers);
return shoppers;
};
ps. bear with me, I'm a junior developer...
Assuming you are trying to filter shoppers that have "at least one review higher than minAverageReview" I think a filter function is enough, you don't need map.
return shoppers.filter((shopper) => {
return
shopper.reviews.some((review) => review.review >= filters.minAverageReview)
});
So in the filter function, you pass a callback that checks for each element and decide to filter out that element or not based on your own criteria, the callback function must return a boolean. The element that doesn't satisfy the criteria (callback returns false) will be filtered out. Here my some() checks if the array (shopper.reviews) has at least one review element that has review higher than minAverageReview.

Superagent Not Returning Value From Then

superagent.get(URL).then((res) => {
for(let i in res.body) {
if (i==='has_rejected_advisories') {
console.log(i + "="+res.body[i]);
}
}
})
.catch((err) => err.message));
My result is:
has_rejected_advisories=false
But I am not able to use res.body[i] outside this function, i.e I want superagent function to return this value in a boolean variable to use it elsewhere.
ex.
a = superagent.get(URL).then((res) => {
for(let i in res.body) {
if(i==='has_rejected_advisories') {
console.log(i + "="+res.body[i]);
}
}
})
.catch((err) => err.message));
if(a===false){/*do this*/}
This is because the superagent.get(url) call is asynchronous. The value given to a is a Promise
Since this is async, the if (a === false) is actually executing before the function body passed to .then. You will either need to move this logic to the .then function, or use something like async/await if you like the synchronous looking syntax.
On top of jerelmiller's great advice you need to note the following:
Try this:
create a global var assuming it's a string
var mysares = ""
This example will only bring back 1 string back of everything!! Not single element. Also if you can't get the standard Fetch() to work don't try other methods like axios or superagents. Now use our global like so:
superagent.get(URL).then((res) => {
for(let i in res.body) {
if (i==='has_rejected_advisories') {
//Add comments as will help you
//to explain to yourself and others
//what you're trying to do
//That said if this iteration produces
//correct data then you're fine
//push my result as a string
mysares = res.body[i];
//infact what's in row 1?
mysares = res.body[0];
//Actually I code my own JSON!!
mysares = res.body[1];
console.log(i + "="+mysares);
}
}
})
.catch((err) => err.message));
Now you can do whatever:
if(mysares===false){/*do this*/
alert(playDuckHunt());}
Things to note:
res.body[i] is an iteration
You cannot use it outside of the function
Because:
It's local to that function
You don't know what position of 'i' is even if you could use it as you will be outside of your loop
One last thing:
Loops loop through loops or arrays etc.
So (in real world) you can't just request the value of the loop
unless you agree the position of data to be released,
type,and bucket (where it's going to be displayed or not).
Hope this helps!
PS> we need to know where 'has_rejected_advisories' is in the JSON so send us your json url as it must be a column/obj header name. Or it's any old 'a' then var a can be your "false"
In constructor:
this.state = {a:null};
In some function:
superagent.get(URL).then(
(res) => {for(let i in res.body)
{
if(i === 'has_rejected_advisories')
{
this.setState({a:res.body[i]})
}
}
}).catch((err)=>(err.message));
In render:
console.log(this.state.a);
Inside then() the value could be used using state variable but there are many scenarios we could not use them, like if we want to perform all the operations under constructor i.e Initializing state variable, calling superagent and changing the state variable and using the state variable.

Resources