angularfirebase2 list query returning undefined - javascript

I must be seriously misunderstanding something with RxJs and AngularFirebase2 because I can't figure out this error.
I have a firebase list that I am querying with a function in a service like so:
returnAuthor(id) {
this.db.list('/flamelink/users', ref => ref.orderByChild('id').equalTo(id)).valueChanges().subscribe(data => { console.log(data); return data })
}
The console.log(data) in returnAuthor produces the correct data, but {{ returnAuthor(id) }} in my template returns undefined. Running returnAuthor in my component also returns undefined.
Can somebody please point me in the right direction here? Do I need to subscribe in the component and not the service?

Your method returns undefined because you are not returning a result from the function.
To return a result, your function will look like this
returnAuthor(id) {
return this.db.list('/flamelink/users', ref => ref.orderByChild('id').equalTo(id)).valueChanges().subscribe(data => { console.log(data); return data })
}
But returning like that will return a subscription not the data. If you want the data from the subscription, you can declare a variable and set the data returned from the subscription to it. Something like this
in your class
dataVariable;
returnAuthor(id) {
this.db.list('/flamelink/users', ref => ref.orderByChild('id').equalTo(id)).valueChanges().subscribe(data => { console.log(data); this.dataVariable = data })
}
Now the data available from the subscription will be passed onto the dataVariable that you can use.
In case you want to pass the data to another method when it arrives, you can call the method in the subscription. Something like this
returnAuthor(id) {
this.db.list('/flamelink/users', ref => ref.orderByChild('id').equalTo(id)).valueChanges().subscribe(data => { console.log(data); anotherMethod(data); })
}

Related

Can't get data from .pipe(map())

Got some problems with observable.
I have a function, with one returns me an Observable.
public getData(userId) {
const data = this.execute({userId: userId});
return {event: "data.get", data: data}
}
private execute(input: SomeDto): Observable<SomeRefType[]> {
return this.databaseGateway.queryMany(DatabaseCommand.WebRecordGetbyparticipantid, {
parameters: {
prm_contextuserid: input.userId,
prm_filterparticipantids: null,
prm_filtertext: null
}
}).pipe(map(res => res));
}
Type what pipe(map) returns
What I'm got when trying to return or log data
Question: Why .pipe(map(res => res)) don't work? What am I doing wrong?
For sure, I can read data from .pipe(take(1)).subscribe(data => console.log(data)), but, how can I return data from construction like this?
Thanks everyone! Have a good day!
As said in the Rxjs documentation observable are lazy computation.
It means the as long as you don't subscribe to them they won't do anything. It exists two ways to trigger a subscription.
Either within a ts file using .susbcribe() or within a view when calling an endpoint.
If you're using nestjs it would be when calling the url defined within a #Controller('') with an http verb like #Get('path')
By convention you suffix observable variables with $: data$ = new Observable<any>().
At some point you'll have to convert the observable to a promise. Best to do it early and convert it using firstValueFrom immediately after the query.
Then convert your caller to an async method to use the returned value.
public async getData(userId): Promise<{ event: string, data: SomeRefType[] }> {
const data = await this.execute({userId: userId});
return { event: 'data.get', data };
}
private execute(input: SomeDto): Promise<SomeRefType[]> {
const res$ = this.databaseGateway.queryMany(DatabaseCommand.WebRecordGetbyparticipantid, {
parameters: {
prm_contextuserid: input.userId,
prm_filterparticipantids: null,
prm_filtertext: null
}
});
return firstValueFrom(res$);
}

Extracting data from subscribe() method

I don't really know how to extract the values from the subscribe() method.
getMessages(): any {
this.gatewayMessagesState$.subscribe(data => data.gatewayMessages
.get(this.gatewayId)
?.list
.map(Message => Message.message));
}
gatewayMessagesState is an initial state that contains some data. gatewayMessages is a map with gatewayIds as keys and arrays of Message objects as values. Message has message field that's just a string. I would like to extract an array of messages for a given id. How can I do that?
What you probably want to do is to populate another Observable with the data so that you can access it elsewhere in your project without the need for calling the API more than once.
To do this, you create what is known as a Subject (in this case a BehaviorSubject) and you can populate that with data when your API call returns a response.
Then, in order to access this data elsewhere, you can create a "get" function to return the Subject (which is itself an Observable) whenever you need the data.
Here is an example:
my - data.service.ts
myData: BehaviorSubject < number > = new BehaviorSubject < number > (0);
callApi() {
this.dbService.get('apiUrl').subscribe(
(data) = > this.myData.next(data) // Assuming data is a 'number'
);
}
getMyData() {
return this.myData.asObservable();
}
Now to use this in a component:
this.myService.getMyData().subscribe(
(data) = > {
/* Use the value from myData observable freely */
}
);
Or you could rely on the Angular async pipe (which is a very convenient method for dealing with observables in your code).
You are not specifying if getMessages is in a service, component... in any case, I suggest returning the Observable without subscribing to it in the getMessages function
// this function could be anywhere
getMessages(): Observable<string[]> {
return this.gatewayMessagesState$.pipe(
map((data) => data.gatewayMessages.get(this.gatewayId)),
map((yourMap) => yourMap?.list.map((theMessage) => theMessage.message))
);
}
Now, if you need to extract this value, either from a component, a service, etc... then, just call this function and then subscribe to get the result
Let's say getMessages is in a service file for example
Your component
constructor(private theService: YourService) {}
anotherFunction() {
this.theService.getMessages().subscribe((myMessages) => console.log(myMessages));
}
Or let the async pipe subscribe to this observable
Your component
messages$!: Observable<string[]>
constructor(private theService: YourService) {}
anotherFunction() {
this.messages$ = this.theService.getMessages()
}
Your component html
<ng-container *ngIf="messages$ | async as messages">
<div *ngFor="let message of messages">
<p>{{ message }}</p>
</div>
</ng-container>
I this you want to retrieve the data as an observable of messages as string, you can define the function return as this and using pipe and map operatoes from rxjs,this is code below is my proposition
getMessages(): observable<string[]>{
return this.gatewayMessagesState$.pipe(map((data) =>
data.filter((f) => f.gatewayMessages.id ===this.gatewayId)),
map(item => item.message));
}

Server Error TypeError: Cannot read property 'map' of undefined

I need help to understand how to fetch external data using server-side rendering with the getServerSideProps method. My real problem is with the search itself, more specifically with the type of data that is returned and how to deal with it. I am not an experienced javascript programmer, so I think this is more of a javascript issue than next.js
This is where I fetch inside getServerSideProps:
export async function getServerSideProps() {
const url = `https://...`;
const options = {
< header info...> };
const res = await fetch(url, options);
const data = await res.json();
const orders = data.list; **// here is where the problem might be**
return { props: { orders } };
}
Here where I render from getServerSideProps:
const ListOrders = ({ orders }) => {
return (
<div>
{orders.map((o, i) => (
<h3 key={i}>{o.orderId}</h3>
))}
</div>
);
};
Here the object that I fetch. The data that interests me is the list.
{
"list":[...]
"data1":[]
"data2":{...}
"data3":{...}
}
I would appreciate any help on that. Thanks in advance!
this error shows data orders is undefined. First thing when you map, in case you dont have any orders array, you have to add a guard to your code, so your app wont break.
<div>
{orders && orders.map((o, i) => (
<h3 key={i}>{o.orderId}</h3>
))}
</div>
this is called short circuiting, orders.map will run if orders is defined.
Since you make an api request. make sure that api server allows your app to do fetcing. Normally you cannot fetch data from external server, unless it allows you.
console.log() does not run inside getServerSideProps(). It is important that you always examine the data that you are fetching. Instead of passing orders pass whole data const data = await res.json() as prop. return { props: { data } }
Now inside the component, console.log(data) see if you ever fetched any data, if yes examine the structure.
You should check structure of orders data before you start using it
U can only map on arrays
The error say that there no variable named undefined
For eg if we call the function and assign result to a variable
let orders = getServerSideProps()
//The data structure of orders variable be like this
/*
orders = {
props : {
orders :{ data1 : [ ], data2 : [ ] }
}
*/ }
// If you map on data1 you need to access like this
orders.props.orders.data1.map(eachdata=>{
// stuff that you want to do here
})

How to assign response object to state

I could get data from nodejs backend to react frontend using axios. But I can't assign that object to state object in React.
getData=()=>{
let responseToHandle;
axios.get("http://localhost:9000/api/getData")
.then(function (response)
{
console.log(response.data);//This is working
responseToHandle = response.data;
console.log(responseToHandle);//This is working
this.setState({
data: responseToHandle}, () => {
console.log(this.state.data)
})
})
// HERE IS MY STATE OBJECT
state={
data:[]
}
axios call and this.setState are both asynchronous.
You must add your desired code inside a callback:
this.setState({
data: responseToHandle
}, () => {
console.log(this.state.data)
// Now you can use this.state.data
});
Edit:
You also need to change
axios.get("http://localhost:9000/api/getData").then(function (response) { ... })
to
axios.get("http://localhost:9000/api/getData").then(response => { ... })
Without arrow function the scope inside .then() is the function scope, different from component scope, which means using this gives you a different value.
This code won't work properly:
console.log(this.state.data)
You called a console.log function synchronously right after a promise declaration. Promise works asynchronously.
So in order to check a new state, you should call console.log in render() method, or the best option is to use componentDidUpdate() method.
E.g.
componentDidUpdate(prevProps, prevState) {
console.log(`Previous data: ${prevState.data}`);
console.log(`New data: ${this.state.data}`);
}

Angular2 observable with JSON root element

This is my first time ever working with angular observables and I'm a bit confused on how this works. I was given a mostly functioning angular CLI app that I just need to wire up to my already existing API.
I have a service with this function
public getApps(): Observable<ApplicationInterface[]> {
return this.http.get(url);
}
Then in my component, I have
public data: ApplicationInterface[];
ngOnInit() {
this.route.params
.subscribe(params => {
this.fetchData();
});
}
fetchData() {
this.service.getApps()
.subscribe(data => {
this.data = data;
});
}
My api endpoint returns a JSON structure of {"applications": []}
I can't seem to figure out how to access the array in that JSON hash.
If I console.log(data) in the subscribe block, it is the API response with the applications key that I expect, but if I change the data assignment to this.data = data.applications, ng build fails with Property 'applications' does not exist on type 'ApplicationInterface[]'
You should design the interface to be aligned with the response. If the response is object, than you need to have it like this also in the interface.
Try something like this (using the new HttpClient):
interface ApplicationInterfaceResponse {
applications: ApplicationInterface[];
}
public getApps(): Observable<ApplicationInterface[]> {
return this.httpClient
.get<ApplicationInterfaceResponse>(url)
.map(response => {
console.log(response.applications);
return data.applications;
});
}
If your return is of type ApplicationInterface[], then it's an array of ApplicationInterfaces, thus does not have a property called applications on it. This has nothing to do with your observable; it's fine. Rather, you've mistyped your variable.
If you don't need any other properties of data, you can map the value:
public getApps(): Observable<ApplicationInterface[]> {
return this.http.get(url).map(data => data.applications);
}
However, I recommend against this in most situations. If your object changes in the future, then you have to change this function and all attached subscriptions. Instead, you should create an interface for your response (your response right now does not match the type you're giving it), and use values of it as necessary.
The simplest fix is to indicate the correct form of the data that is returned by your service method since it doesn't actually return an array:
public getApps(): Observable<{applications:ApplicationInterface[]}> {
return this.http.get(url);
}
Now in your subscribe, you can get at the array as you would expect
.subscribe(e => this.data = e.applications)

Categories

Resources