Can't map HTTP response to Interface - angularjs

The following code returns on Chrome console: ERROR TypeError: t.json(...).map is not a function The ng serve -prod and ng test --sm=false runs fine.
I want to map the result to the model in Interface, and output them in HTML.
....
export interface UsersData {
_id: number;
email: string;
fullName: string;
}
export class RetrieveUsrData {
private issuesUrl = 'http://localhost:4000/users';
getUserDetails(): Observable<UsersData[]> {
return this.http.get(this.issuesUrl)
.map(this.extractData)
}
extractData(result: Response): UsersData[] {
console.log(JSON.stringify( result.json()) );
return result.json().map(usr => {
return {
_id: usr.message._id,
email: usr.message.email,
fullName: usr.message.fullName
}
}).subscribe(result => result = result);
}
constructor(private http: Http) {}
}
...
I have been checking in few issues e.g. mapping a HTTP response to a JSON and then mapping the JSON to a model, and changed my code/method to:
extractData(result: Response): UsersData[] {
return result.json().map(res => ({
_id:res.message._id,
email:res.message.email,
fullName:res.message.fullName
}) as UsersData)
.subscribe(result => result = result); ;
}
but I keep getting the same error.

The .map method is available on arrays, not objects.
The most likely cause of your error is that if you have a response like this:
{
someKey: [1, 2, 3, 4]
}
You will need to use:
result.json().someKey.map( //...
And you may even be able to simply use the following if the JSON actually adheres to your interface:
return <UsersData[]>result.json().someKey;

Related

How do I get a series of documents from Firestore and return them all in an array?

My Firestore data has the following structure:
topics collection containing documents with the following structure:
id: string
name: string
tag: string
infoID: string
these map to an ITopic interface:
interface ITopic {
id: string
name: string
tag: string
infoID: string
}
infos collection containing documents with the following structure:
id: string
content: string
topicID: string or null
which map to:
interface IInfo {
id: string
content: string
topicID: string | null
}
So each topic document has an associated info document, and vice-versa (ie a one-to-one mapping).
I've retrieved all my topic documents from the database and now I want to get the associated info for each one, all in an array. So each item in the array would be an object with topic and info fields:
interface ITopicInfo {
topic: ITopic
info: IInfo
}
I've tried this (topics is the existing array of all my topics):
async function getTopicsInfo(db: Firestore, topics:Array<ITopic>) {
try {
let topicsInfo:Array<ITopicInfo> = topics.map((topic) => {
const infoRef = doc(db, 'infos', topic.infoID);
const infoSnap = await getDoc(infoRef);
if (infoSnap.exists()) {
const result = infoSnap.data();
const newTopicInfo:ITopicInfo = {
topic: topic,
info: {
id: result.id
content: result.content
topicID: result.topicID
},
};
return newTopicInfo;
} else return null;
});
return topicsInfo;
} catch (error) {
throw error;
}
}
However, I'm getting a TypeScript error saying
Type '(Promise<ITopicTree | null> | null)[]' is not assignable to type '(ITopicTree | null)[]'.
it looks like it's returning the Promise rather than the result, basically. How do I fix this?
did you try to return map directly :
instead of doing this :
let topicsInfo:Array<ITopicInfo> = topics.map((topic) => { ...
Just do this :
return topics.map((topic) => { ...
UPDATE :
async function getTopicsInfo(db: Firestore, topics:Array<ITopic>): ITopicInfo[] { ...

Graphql mutation query : how to access the data elements

const MUTATION_QUERY = gql`
mutation MUTATION_QUERY(
$name: bigint!
) {
insert_name(
objects: {
name: $name
}
) {
returning {
id
name
}
}
}
`;
const [onClick, { error, data }] = useMutation<{}, {}>(MUTATION_QUERY, {
variables: {
name: 1234,
},
});
My mutation query is inserting name in my table and autogenerating the id. On console logging the data variable I can view the fields id and name in the data object. But I am not able to access them them individually. How can I console.log "id". Thank you.
the console.log(data) looks like : {insert_name: {...}}
which expands to :
insert_name:
returning: Array(1)
0: {id: 1, name: 1234}
length: 1
_proto_: Array(0)
_typename: "insert_name_mutation_response
You can access the fields of an object with .
For example, if your object looks like this -
data = {
id: 1,
name: 'Jane',
}
You can get just the id with data.id
This works no matter how many layers deep your object may go, so take this example -
data = {
person: {
id: 1,
name: 'Jane',
}
}
You could get the id of person with data.person.id.
console.log(data.insert_name.returning[0].id) will give you the id returned.
For it to work in typescript we need to change the query to add the return type of data
const [onClick, { error, data }] = useMutation<{ReturnInsertNameProps}, {}>(MUTATION_QUERY, {
variables: {
name: 1234,
},
});
interface ReturnInsertNameProps {
insert_name: ReturnQueryProps;
}
interface ReturnProps {
returning: MessageProps[];
}
interface NameProps {
id: number;
name: number;
}
We can also use onCompleted method provided in useMutation if we want to process the result of the query.

mapping from rest response

I am working in an example with the Dog Api (https://dog.ceo/api/breeds/list/all), the response for the breeds endpoint is something like that:
{
"message": {
"affenpinscher": [],
"african": [],
"airedale": [],
"akita": [],
"appenzeller": [],
"australian": ["shepherd"]
}
I need that my breed service, returns an observable of Breed
export class Breed {
name : String;
subbreeds : String[]
}
This call is returning an object with the property message. How I can map in my service for return an Observable<Breed[]> from that API Rest response.
getAll() : Observable<Breed[]> {
return this.http.get<Breed[]>("https://dog.ceo/api/breeds/list/all");
}
Actually this question is nothing about rxjs and angular 8, it about JavaScript.
export class Breed {
// public modifier is a short hand for this.name = name
constructor(public name: string, public subbreeds: string[]) {}
}
class Service {
getAll() {
return this.http
.get<Breed[]>('https://dog.ceo/api/breeds/list/all')
.pipe(
map(data => // map here is a rxjs operator, do not confuse with JS array's map
// Object.entries is convert Object's key value to entries [key, value] array
Object.entries(data.message).map( // JS array map method mapping each entry to Breed instance
// destructing from entry and return a Breed instance
([breed, subbreed]) => new Breed(breed, subbreed)
),
),
);
}
}
If you not sure about any term or method, please check MDN and RxJS official document

How to handle returning array with forkJoin() in Angular?

I'm using forkJoin() to handle multiple observables with this code:
forkJoin([
this.userRoleService.getAll(), // result is an array of UserRole models
this.userService.getOne(id), // result is a single User model
this.countyService.all(), // result is an array of County models
]).subscribe(([userRoles, userModel, counties]) => {
console.log(userRoles, userModel, counties);
// handle the result
});
As you see in results I need to get two arrays and a single object. But in this scenario I get this in the console:
(2) [UserRole, UserRole]
UserModel {api_endpoint: "user/", role: UserRole, id: 1, name: "admin", email: "admin#admin.test", …}
CountyModel {id: 20, name: "Hazard"}
Here I got one array with two of UserRole instances, one UserModel instance and one CountyModel instance.
Here is the county.service.ts:
import { Injectable } from '#angular/core';
import { CountyModel } from 'src/app/models/County.model';
#Injectable({
providedIn: 'root'
})
export class CountyService {
db: CountyModel[] = [];
constructor() {
const items = JSON.parse( localStorage.getItem('counties'));
items.forEach( (item: any) => {
this.db.push(new CountyModel().init(item));
});
}
all(): CountyModel[] {
return this.db ? this.db : [];
}
}
So the service's all() method return with an array in every case. But why I get only the last element of this array as result in the forkJoin and how can I catch all of the array elements?
You are not returning observable of array in countyService, try wrap it with of()
forkJoin([
this.userRoleService.getAll(), // result is an array of UserRole models
this.userService.getOne(id), // result is a single User model
of(this.countyService.all()), // result is an array of County models
])

How to extract array from json on Angular

I'm working with angular2 ( version 5).
I make an http request an get back json.
I know how to access and use value but not the array.
and I don't find how to extract the two array inside element.
here my json:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
},
{
"nomeImpianto":"MFL2",
"descrImpianto":"Multifilo 2",
"posizione":"Place2",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 12.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
}
]
}
In the data.service.ts I have the http request and it store values on :
stream$: Observable<ImpiantoModel[]>;
here my definition of the model:
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel[];
orario: OrariModel[];
}
#orari.model.ts
export class OrariModel {
orario: string;
}
#velocita.model.ts
export class VelocitaModel{
vel : number;
}
is it the right why to define my object?
How can I use the array "vel" and "orario"?
How can I print (access) the array "vel" of machine with "nomeImpianto" = "MFL1" ?
and how can I copy the array "vel" on new array?
thank you very much!
Here is what I understood of what you want to do : get the item in your json resp and put it in your object , so the best way is to create a static method directly when you get the json response, before returning the value create this adapter adaptImpiant(jsonObj) which will do something like :
adaptImpiant(jsonObj) {
let impiantTab = [];
jsonObj.ImpiantiProva.forEach((item) => {
let impiantoModel = {};
// impiantoModel = item if the model (below) match the item;
// if not manually set all your var like your velocita
let velocita = [] // is an array or an object with an array
// if class velocita = {}
velocita = item.vel.slice(0);
// if class velocita.valuesTab = item.vel.slice(0);
impiantoModel.velocita = velocita;
impiantTab.push(impiantoModel);
}
}
Your model seems wrong in this case, because you already use a ImpiantoModel array, so just create a class with whatever you want in :
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel // or simply [];
orario: OrariModel // or simply [];
}
I'm not sure I understand you, but I'll try.
is it the right why to define my object?
It should be:
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: Array<string>;
orario: Array<string>;
}
(But I have to confess, I don't know why model and not an interface)
How can I use the array "vel" and "orario"?
What do you mean?
How can I print (access) the array "vel" of machine with
"nomeImpianto" = "MFL1"
const thisContainsTheDataFromVel = whereYourDataIsStored['ImpiantiProva'].find((item) => { item['nomeImpianto'] === 'MFL1'})['vel'];
and how can I copy the array "vel" on new array?
UPDATE after reading your comment under this answer:
I took code from your example and added what you are missing. I made it so it can be more reusable (it can be enhanced even more, but I hope you understand the code and do what you need).
copyArray(data, targetValue) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
return mfl1Data.vel;
}
return [];
}
getdata2() {
this.http.get<ImpiantoModel[]>(this.myUrl)
.subscribe(
data => {
this.variableToStoreIn = this.copyArray(data, 'MFL1');
data.forEach(item => {
this.sub$.next(item);
});
});
return this.sub$;
}
CopyArray finds the data and returns it. If you don't want it like this, but just set a value of some property to the value of vel array then you can change it to:
copyArray(data) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
this.yourVariable = mfl1Data.vel;
}
}
If this answer is sufficient, please consider to mark it as the best answer, thank you.
According to your model classes, your JSON is wrong. You should have something like this:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [
{
"vel": 24.5
},
{
"vel": 13.6
}
...
],
"orario": [
{
"orario": "17.05"
},
{
"orario": "17.06"
}
...
]
}
]
}
Your model expects ImpiantoModel.vel and ImpiantoModel.orario to be arrays of objects. In your JSON response one is an array of numbers and the other of strings.
An if you want to use it in an HTML template, considering that you have a class attribute in your .ts file like this:
private impiantoModels: ImpiantoModel[];
You could do something like this inside your .html template:
<div *ngFor="let impModel of impiantoModels">
...
<div *ngFor="let v of impModel.vel">
<p>{{v.vel}}</p>
</div>
<div *ngFor="let o of impModel.orario">
<p>{{o.orario}}</p>
</div>
</div>

Resources