0

I am trying to retrieve data from the backend. These are the relevant parts of my code:

API call

getData(PrimaryId:number):Observable<DataDto[]>{
  return this.httpClient.get(`${this.prefix}/<xyz>/${PrimaryId}/xyz`) as Observable<DataDto[]>
}

Component TypeScript

onRetrieveClicked() {
  this.xyzService.getData(this.PrimaryId).subscribe(
    (xyz: DataDto[]) => {
      this.xyz = xyz
      console.log(this.xyz)
      console.log(this.xyz.forEach((data)=>data.name) 
  })
}

First console.log output

{content: Array(1), pageable: {…}, totalPages: 1, totalElements: 1, last: true, …}
       content: Array(1)
       0: {name: max, name: null, asset: null,  …}
       length: 1
       ..........

But when I try to print only the name in the second console, it says that forEach is not a function. How can I solve this

edit Dto model

export interface DataDto {
name: string
asset: abcDto 
status: StatusDto 
tasks: efgDto[]
nextDate: string
}
9
  • assumingly you want to call forEach for content which is present in your data..... Commented Sep 19, 2019 at 11:54
  • It seems like the data you get might be a plain object, not an array. It has some arrays as values, though. Commented Sep 19, 2019 at 11:55
  • I tried accessing it this way. this.xyz.content.map(data=>data.name) I get the output but also in the iDE, there is an error saying content does not exist on type DataDto[] Commented Sep 19, 2019 at 12:00
  • xyz: DataDto[] is misleading the TS compiler. If you are indeed getting a plain object at runtime, then TS doesn't know that, it thinks you are getting an array but it can only check that at compile time. Commented Sep 19, 2019 at 12:03
  • Check my answer, it explains entirely what's going on here, and how to fix it using Angular preferred operations. Commented Sep 19, 2019 at 12:07

3 Answers 3

1

The xyz variable that you type as DataDto[], an array, is actually an object. This can be seen in your console.log, an array would be enclosed in [], not {}

is an object --> {
  content: Array(1), pageable: {…}, totalPages: 1, totalElements: 1, last: true, …}
       content: Array(1)
       0: {name: max, name: null, asset: null,  …}
       length: 1
 } 

The data you are looking for is most likely the response object's content so add an import for import {map} from 'rxjs/operators'; and transform the data you've gotten from the response:

this.xyzService.getData(this.PrimaryId).pipe(
  map((xyzResponse: any) => xyzResponse.content)
).subscribe(
  (xyz: DataDto[]) => {
    this.xyz = xyz;
    console.log(this.xyz);
    let dataNames = xyz.map(data => data.name);
    console.log(dataNames);
}

I've typed xyzResponse as any but you could ofcourse create a reusable type for it if the API always returns the object with content, pageable, totalPages, ...

Rxjs is the library that Angular uses to handle asynchronous programming, such as HTTP calls or component events. Rxjs chains asynchronous manipulations together in a pipe (hence the .pipe call). Inside of this pipe rxjs expects a chain of operators that will perform operations on the asynchronous data, one by one. The map operator takes the input value and returns a new value so that the value you subscribe to has been transformed from the HTTP response to the .content field of the HTTP response.

Working in this way fixes all TypeScript compiler errors and allows you to chain additional calls later, like retrying if the API times out, or catching errors, or merging in other HTTP calls.

Sign up to request clarification or add additional context in comments.

3 Comments

it returns undefined
What does? The 2nd console.log? That's your code, but yea it'll print that, because forEach doesn't return a value :p
If you want to see each data.name use this: this.xyz.forEach((data)=>{console.log(data.name)})
1

It seems that your this.xyz is not an array, but has an array property called content, you should modify your response object in order to accept it.

You can check if your objects are arrays with the following method

Array.isArray(obj)

Update your code to this.

this.xyzService.getData(this.PrimaryId).subscribe(
  (xyz: NewObject) => {
    this.xyz = xyz
    console.log(this.xyz)
    //If you have doubts of what is comming is nice to check if your property is an array
    if(Array.isArray(this.xhy.content) {
        console.log(this.xyz.content.forEach((data)=>data.name) });
    }
}

Create a new object in order to support your response

class NewObject {
    content: Array<DataDto>
    // other values here
}

Another approach is like @Robin says in the comment

this.xyzService.getData(this.PrimaryId).subscribe((xyz: {content: DataDto[]}) => 
        {
        this.xyz = xyz
        console.log(this.xyz)
        //If you have doubts of what is comming is nice to check if your property is an array
        if(Array.isArray(this.xhy.content) {
            console.log(this.xyz.content.forEach((data)=>data.name) });
        }
    }

2 Comments

error TS2339: Property 'content' does not exist on type 'DataDto[]
Well if you wanna cut corners you could just do: .subscribe((xyz: {content: DataDto[]}) => {console.log(xyz.content.forEach((data)=>data.name))})
0

It's because you are trying to loop through an object instead of array I think you can try this:

console.log(this.xyz.content.forEach((data)=>data.name) })

1 Comment

error TS2339: Property 'content' does not exist on type 'DataDto[]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.