Return Array from Firebase snapshot method - arrays

I'm connecting to Firebase and retrieving data from a given location, what I'm trying to do is loop through the snapshot.val() build and Array, return the Array and then loop through it on the component.html and build a Dropdown.
At present I'm having difficulty figuring out the syntax for such service method, this is my current code. - This is called from app.component.ts within ngOnInit()
getExerciseOptions(): Array<Exercise> {
const items: Exercise[] = [];
this.exerciseDbRef2.on("value", (snapshot) => {
snapshot.forEach((snap) => {
items.push({
id: snap.key,
description: snap.val().description
});
return false;
});
});
}
So this.exerciseDbRef2 points to the table within Firebase as shown here:
private exerciseDbRef2 = this.fire.database.ref('/exercise_description');
The error I'm currently receiving is
A function whose declared type is neither 'void' nor 'any' must return a value.
Which I understand, so when I change return false to return items the new error is:
Argument of type '(snap: DataSnapshot) => Exercise[]' is not assignable to parameter of type '(a: DataSnapshot) => boolean'.
Type 'Exercise[]' is not assignable to type 'boolean'.
I have looked at using child_added but from my understanding this will be called every time a new child has been added into that location, which is not what I'm looking for. The children in this location will not change nor will any be added. - Maybe I've misinterpreted 'child_added' ?
I'm rather new to Firebase so I'm at the beginning on the learning curve so please bare with, I would also like to mention if the way I'm currently doing this is not correct then please bring it to my attention.
So for clarification : Connect to Firebase, retrieve all children from given location i.e exercise_description table, loop through the snapshot, build an Array and return it.
Then on the component loop through the Array and build the Dropdown.
Can someone please explain how I go about returning the Array based off the snapshot.val() ?

You cannot return an array from getExerciseOptions, as the value event is asynchronous.
However, you could return a promise:
getExerciseOptions(): Promise<Array<Exercise>> {
return this.exerciseDbRef2
.once("value")
.then(snapshot => {
const exercises: Exercise[] = [];
snapshot.forEach(snap => {
exercises.push({
id: snap.key,
description: snap.val().description
});
return false;
});
return exercises;
});
}
You would then call it like this:
getExerciseOptions().then(exercises => console.log(exercises));
If you are unfamiliar with promises, you might want to read JavaScript Promises: an Introduction.

Related

Angular typescript iterate over results returned from service call

I am trying to iterate over an "array" that's being returned from a result set in order to process the objects contained within.
I have tried various approaches including data.forEach(), const myStuff = {...data} and for (let item of data) {} in both the "success" function of the .subscribe and in the processData function, but in all cases I'm unable to extract the values desired.
DevTools reports the result to be Array(51) but it sure doesn't want to process like a typical Array.
submitCarrierSearchForm = (): void => {
this.carrierService
.carrierGetCarrierListByOrgCode({
organizationCode: this.searchForm
.get('carrierSearch')
?.get('organizationCode')?.value
})
.pipe(takeUntil(this.destroy$))
.subscribe(
(data) => {
// this.processData(data);
console.log(data);
},
(error: unknown) => {
console.log('Errors encountered: ', error);
},
);
};
processData(carriers: Carrier): void {
console.log('coming from data: ', carriers);
}
Errors vary, including:
Property 'forEach' doesn't exist on type (lists all properties of the object) when using
const stuff = { ...carriers };
stuff.forEach(element => {
console.log(element.carrierName);
});
No doubt, it's something simple that I'm missing.
It seems that you declare 1 item in stead of an array of items:
maybe try this:
processData(carriers: Carrier[]): void {
console.log('coming from data: ', carriers);
}
Turned out to be an error in the return type of the API which was preventing the object from actually being an array. Once the return type was corrected, all the "normal" ways of manipulating an array worked as expected.

TypeScript How To Loop Through Array Of Objects

I'm currently busy with an Angular project where I receive an object containing an array of objects from an API.
"reportData" is the object being sent to the API as a parameter through my service.
Example of data sent back from the API:
I want to loop through the array and push the product names to an array and the same with the averageQuantityOrdered but I don't know how to loop through the array and get the values.
One thing that is confusing me is that when you look at the attached screenshot it looks like the result is an object containing an array of objects. And I think that is why I can't loop through it, since technically its an object and not an array.
I tried something like this (retrievedData is the array of objects) but then I get the error "Cannot read property 'forEach' of undefined":
retrievedData: any;
this.retrievedData.array.forEach(element => {
this.productNames.push(element.ProductName);
});
I use a service to retrieve the data:
onSubmit(form:NgForm)
{
this.service.postReportData().subscribe(
res => {
this.retrievedData = res;
console.log(this.retrievedData);
},
err => {
console.log(err);
}
);
}
Any help would be appreciated!
so what you receive is the an object containing array of objects
your res should be of type
type Item = {
ProductName: string;
AverageQuantityOrdered: number;
ProductOrders: unknown[];
}
type RetrievedData = {
reportData: Item[];
}
so you can set RetrievedData as a type to your retrievedData variable
retrievedData: RetrievedData
instead of any
and then retrievedData.reportData will give you the array.
Thank you everyone for your useful comments!
I got it working with the following code:
onSubmit(form:NgForm)
{
this.service.postReportData().subscribe(
res => {
this.retrievedData = res;
this.retrievedData.reportData.forEach(function (item) {
console.log(item);
console.log(item.ProductName);
console.log(item.AverageQuantityOrdered);
});
},
err => {
console.log(err);
}
);
}
Since reportData contained the array of objects I had to loop through that and not retrievedData.
Once again thanks to everyone, I appreciate the help!

React Promise Return Firebase Array

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;
})
}

Converting Observable<B> to object B

I am using Angular 2 with RxJs. I get the following compilation exception,
[ts]
Type 'Observable<Observable<B>>' is not assignable to type
'Observable<B>'.
Type 'Observable<B>' is not assignable to type 'B'.
Property 'id' is missing in type 'Observable<B>'.
Below is the code in question. I can't really change the method signature for 'getB' since I am overriding it. The base method does not need the 2nd API call to fill children, hence overriding.
export interface A {
b: B;
}
export class B {
cList: C[];
}
export interface C {
name: string;
type: string;
}
getB(): Observable<B> {
let entries = <A[]> []; // get A[] via an server call
// in this case entries will have only one element in the array
return this.loadBChilds(entries[0])
.map(e => e.b);
}
loadBChilds(a: A): Observable<A> {
// API call to fetch childs
// set childs to a.b.List
return this.rest.get('someapi')
.map(res => {
res.record.map(r => this.setData(r));
return a;
});
}
Any idea how to achieve this?
TIA
Managed to solve this. The fix was to collapse the first array being received from the first API via a flatMap and then use that for subsequent API call.
getB(): Observable<B> {
return this.rest.get('firstapi')
.flatMap(res => {
let entries = res.record
.map(record => this.getEntry(record));
return this.loadBChilds(entries[0])
.map(e => e.b);
});
}

Async array without using anything async?

It drives me crazy. I have a really simple "problem" and it took me hours and still have no idea whats going on.
I have a child service which inherits from a parent service (I'm using ES6). The constructor takes an 1 argument called options. options will be assigned to this._defaults.
Now before I pass the options into my object (new Service(options)) I populate options with some data. To keep it simple, my current options object looks like this:
const options = {
types: []
}
Now I add some stuff into the types array, like this:
const Types = {
standard: {
some: 'data'
},
freeroll: {
some: 'data'
},
mainevent: {
some: 'data'
},
qualifier: {
some: 'data'
}
};
angular.forEach(Types, (val, key) => {
options.types[key] = true;
});
I assign my service to the scope like this:
$scope.service = new Service(options)
and output the service using console. The console now says the value of _defaults.types is Array(0). When I click on the array the correct values will be shown but the scope is not aware of that.
How is that? Doesn't Array(0) mean that at the time of the console.log() the array wasn't filled with any values but has been later? Like an async function would do?
Here is a plunk of my problem.
The problem is that types is an array and you're treating it like a plain Object. You can solve this one of two ways.
First just change types to an Object:
const options = {
types: {}
};
Or, if you need an Array, change how you're adding items to the array:
angular.forEach(Types, (val, key) => {
options.types.push({
type: key,
value: val
});
});
Note that this is just one way of turning the object into an array, the data structure you end up with is up to you.

Resources