0
votes

I am not so into RxJs. I am following an Angular video course based on Angular+RxJS and I have the following doubt. Basically it creates a service class that call a REST API returning a JSON like this:

{
   "payload":[
      {
     "id":1,
     "description":"Serverless Angular with Firebase Course",
     "longDescription":"Serveless Angular with Firestore, Firebase Storage & Hosting, Firebase Cloud Functions & AngularFire",
     "iconUrl":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/serverless-angular-small.png",
     "lessonsCount":10,
     "category":"BEGINNER",
     "seqNo":6,
     "url":"serverless-angular",
     "price":50
      },
      {
     "id":2,
     "description":"Angular Core Deep Dive",
     "longDescription":"A detailed walk-through of the most important part of Angular - the Core and Common modules",
     "iconUrl":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-core-in-depth-small.png",
     "lessonsCount":10,
     "category":"BEGINNER",
     "seqNo":3,
     "url":"angular-core-course",
     "price":50
      },
      {
     "id":3,
     "description":"RxJs In Practice Course",
     "longDescription":"Understand the RxJs Observable pattern, learn the RxJs Operators via practical examples",
     "iconUrl":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/rxjs-in-practice-course.png",
     "courseListIcon":"https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png",
     "category":"BEGINNER",
     "lessonsCount":10,
     "seqNo":2,
     "url":"rxjs-course",
     "price":50
      },
      {
     "id":4,
     "description":"NgRx (with NgRx Data) - The Complete Guide",
     "longDescription":"Learn the modern Ngrx Ecosystem, including NgRx Data, Store, Effects, Router Store, Ngrx Entity, and Dev Tools.",
     "iconUrl":"https://angular-university.s3-us-west-1.amazonaws.com/course-images/ngrx-v2.png",
     "category":"BEGINNER",
     "lessonsCount":10,
     "seqNo":1,
     "url":"ngrx-course",
     "promo":false,
     "price":50
      },
      {
     "id":5,
     "description":"Angular for Beginners",
     "longDescription":"Establish a solid layer of fundamentals, learn what's under the hood of Angular",
     "iconUrl":"https://angular-academy.s3.amazonaws.com/thumbnails/angular2-for-beginners-small-v2.png",
     "courseListIcon":"https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png",
     "category":"BEGINNER",
     "lessonsCount":10,
     "seqNo":4,
     "url":"angular-for-beginners",
     "price":50
      },
      {
     "id":6,
     "description":"Angular Security Course - Web Security Fundamentals",
     "longDescription":"Learn Web Security Fundamentals and apply them to defend an Angular / Node Application from multiple types of attacks.",
     "iconUrl":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/security-cover-small-v2.png",
     "courseListIcon":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/lock-v2.png",
     "category":"ADVANCED",
     "lessonsCount":11,
     "seqNo":9,
     "url":"angular-security-course",
     "price":50
      },
      {
     "id":7,
     "description":"Angular PWA - Progressive Web Apps Course",
     "longDescription":"Learn Angular Progressive Web Applications, build the future of the Web Today.",
     "iconUrl":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-pwa-course.png",
     "courseListIcon":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/alien.png",
     "category":"ADVANCED",
     "lessonsCount":8,
     "seqNo":10,
     "url":"angular-pwa-course",
     "price":50
      },
      {
     "id":8,
     "description":"Angular Advanced Library Laboratory: Build Your Own Library",
     "longDescription":"Learn Advanced Angular functionality typically used in Library Development. Advanced Components, Directives, Testing, Npm",
     "iconUrl":"https://angular-academy.s3.amazonaws.com/thumbnails/advanced_angular-small-v3.png",
     "courseListIcon":"https://angular-academy.s3.amazonaws.com/thumbnails/angular-advanced-lesson-icon.png",
     "category":"ADVANCED",
     "seqNo":11,
     "url":"angular-advanced-course",
     "price":50
      },
      {
     "id":9,
     "description":"The Complete Typescript Course",
     "longDescription":"Complete Guide to Typescript From Scratch: Learn the language in-depth and use it to build a Node REST API.",
     "iconUrl":"https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-small.png",
     "courseListIcon":"https://angular-academy.s3.amazonaws.com/thumbnails/typescript-2-lesson.png",
     "category":"BEGINNER",
     "seqNo":12,
     "url":"typescript-course",
     "price":50
      },
      {
     "id":11,
     "description":"Angular Material Course",
     "longDescription":"Build Applications with the official Angular Widget Library",
     "iconUrl":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/material_design.png",
     "category":"BEGINNER",
     "seqNo":14,
     "url":"angular-material-course",
     "price":50
      },
      {
     "id":12,
     "description":"Angular Testing Course",
     "longDescription":"In-depth guide to Unit Testing and E2E Testing of Angular Applications",
     "iconUrl":"https://s3-us-west-1.amazonaws.com/angular-university/course-images/angular-testing-small.png",
     "category":"BEGINNER",
     "seqNo":5,
     "url":"angular-testing-course",
     "lessonsCount":10,
     "promo":false,
     "price":50
      },
      {
     "id":14,
     "description":"NestJs In Practice (with MongoDB)",
     "longDescription":"Build a modern REST backend using Typescript, MongoDB and the familiar Angular API.",
     "iconUrl":"https://angular-university.s3-us-west-1.amazonaws.com/course-images/nestjs-v2.png",
     "category":"BEGINNER",
     "lessonsCount":10,
     "seqNo":8,
     "url":"nestjs-course",
     "promo":false,
     "price":50
      },
      {
     "id":16,
     "description":"Stripe Payments In Practice",
     "longDescription":"Build your own ecommerce store & membership website with Firebase, Stripe and Express",
     "iconUrl":"https://angular-university.s3-us-west-1.amazonaws.com/course-images/stripe-course.jpg",
     "lessonsCount":10,
     "category":"BEGINNER",
     "seqNo":7,
     "url":"stripe-course",
     "price":50
      },
      {
     "id":17,
     "description":"Reactive Angular Course",
     "longDescription":"How to build Angular applications in Reactive style using plain RxJs - Patterns and Anti-Patterns",
     "iconUrl":"https://angular-university.s3-us-west-1.amazonaws.com/course-images/reactive-angular-course.jpg",
     "courseListIcon":"https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png",
     "category":"BEGINNER",
     "lessonsCount":10,
     "seqNo":0,
     "url":"reactive-angular-course",
     "price":50
      }
   ]
}

As you can see it contains a payload property containing n objects. The tutorial show as, starting from the Observable emitting this payload property it will return another Observable that emit only the array.

This is the proposed solution:

@Injectable({
  providedIn: 'root'
})
export class CoursesService {

  constructor(private http:HttpClient) {}

  loadedAllCourses(): Observable<Course[]> {
    // The obsarvable return a JSON containing the "payload" property containing the array
    //return this.http.get<Course[]>("/api/courses");
    return this.http.get<Course[]>("/api/courses")
          .pipe(
            map(res => res["payload"])
          );


  }

}

Now I know that the map() operator works on an Observable and return another Observable "modified" by the arrow function passed to the origianl Observable (in this case I am accesing to the content of the payload field of the API call response and I am creating a new Observable containing only the payload field content. It is clear.

The thing that I can not understand is: why this map() operator is defined inside a pipe() operator. From what I understood reading the official documentation the pipe() operator is used to chain multiple operators in declarative fashion (so for example: apply OPERATOR 1, on the output of the OPERATOR 1 (that is an Observable) apply OPERATOR 2 and return another Observable and so on.

Is this reasoning correct? In the previous case I only have a map() operator so in theory I can use the map() operator directly on the Observable returned by the get() method (directly on the Observable containing the API response). Is it right?

Why in this course the istructor always use the pipe() operator also when only a single operator is applied? (as in the previous case). Is it a sort of convention or is it an "error"?

1
There are at least two distinct questions here. Your intuition is correct. The following is valid map(x => x.payload)(myObservable$). Why don't most people use that form for applying a single operator? I would argue it's because it reduces readability by employing a different syntax for the unary case, makes adding an additional operator inconvenient, and most importantly because of how the rxjs API evolved over time from fluent method calls to a functional pipeline. As you say, that's a convention. It should be noted that your use of types is incorrect in that would be the other question...Aluan Haddad
@AluanHaddad Tnx, why did you said that the use of types is incorrect?AndreaNobili
@AndreaNobili With this.http.get<Course[]>("/api/courses") you specify that this http request returns Course[]. But res is not of type Course[], res[payload] is.frido
@fridoo thank you. That's exactly what I meant.Aluan Haddad

1 Answers

-1
votes

In this course the instructor always use the pipe() operator because starting from RxJs 6 they use pipeable operators and that goes from official documentation - https://rxjs.dev/guide/v6/pipeable-operators.

Regarding the way how you use map, seems like "pluck" operator is more appropriate for your case:

   this.http.get<Course[]>("/api/courses")
    .pipe(
        pluck("payload")
    );