Ravi Khambhati
Ravi Khambhati

Reputation: 743

Not getting expected result from RxJS map() operator with Angular HttpClient

I am trying to understand the map() operator of RxJS.

Below is one of the example with works as expected. In this example all names will be appended with text student one by one.

studentArray:string[]=['sam','robert','lisa'];
from(this.studentArray).pipe(map((s:string)=>s=s+' student')).subscribe(d=>console.log(d));

Now I have below sample code. With this code I am calling a service which returns array of ToDo objects. What I am trying to do is the iterate each ToDo object and return the updated object

enter image description here

But when I do so it gives an error and reason for the error is data variable in the map operator. With above example map operator is not getting single element of an array of ToDo but the entire array is being passed to the map.

Am I doing something wrong here?

Edit: Updated image.

Upvotes: 1

Views: 1908

Answers (4)

Jason White
Jason White

Reputation: 5813

Follow @Vasileios Kagklis's answer. He explains your issue. The rxjs map pipe is completely different than the JavaScript higher order map function. You shouldn't be assigning a value in the map pipe with =.

I'm not 100% sure what you're trying to accomplish with this but from what I can understand you might be looking for something like...

this.http.get<ToDo[]>(`....`)
  .pipe(
    map(data => data.map(todo => ({
       ...todo,
       title: todo.title + 'append'
     })
  )
  .susbscribe(todos => console.log(todos));

So the map rxjs pipe, receives a ToDo[], so data is a ToDo[] not a single ToDo. So once the data is emitted you have to iterate over that array and mutate each item in the array (JavaScript's higher order map function) updating the title.

Upvotes: 0

Vasileios Kagklis
Vasileios Kagklis

Reputation: 826

You are using the map operator incorrectly. The map operator receives a value, transforms it, and returns the transformed value. You are doing an assignment instead. A valid example would be:

.pipe(
   map((value: string) => value + 'append')
)

Also, your data is not a single ToDo but an array, i.e. ToDo[]. So with data = data.title + 'append' you are trying to assign a string to a ToDo[].

If I understand correctly, you want to append something on each ToDo's title property. Then you would have to do something like this:

this.http.get<ToDo[]>('YOU-ENDPOINT-URL-HERE').pipe(
   concatMap((dataArray: ToDo[]) => from(dataArray)), // from array to single objects
   map((data: ToDo) => {
      data.title += 'append'; // transform tittle
      return data; // return transformed data
   }),
   toArray() // recreate array
).subscribe((d) => console.log(d));

EDIT: Your first example is not equivalent to your use-case. The from operator takes an array, in your example string[], and converts it into an Observable<string>.

The http.get<Todo[]> declares that the returned type from your backend is going to be an array. And this is the type of data that will be passed to your pipe, hence to your map operator as well.

Upvotes: 0

onrails
onrails

Reputation: 849

It is not Angular-ish thing per say but it is more a Typescript compiler issue. It is doing its job correctly telling you that you are assigning something of type B to something else of type C. In our case here, it is the fact that you are assigning a string to an array of TODOs.

What @Jason White mentioning in the comment is IMO the correct answer to solve your problem.

Upvotes: 1

fujy
fujy

Reputation: 5264

map() function that you use is not JavaScript standard Array.map, it is the RxJs map operator, it applies a project function to each value emitted (in your case, a single value is an array of ToDo) by the source Observable, and emits the resulting values as an Observable.

Upvotes: 3

Related Questions