When dealing with nested subscriptions in your code, it often indicates a problem. Instead of using nested subscriptions, consider utilizing higher order observables like switchMap, concatMap, mergeMap, zip, and others.
To help you better understand the concept, I have created a comprehensive demonstration of the desired outcome.
Let's begin by defining the necessary interfaces for improved type safety:
export interface Car {
id: string;
seatId: string;
tyreId: string;
}
export type CarResolved = Omit<Car, "seatId" | "tyreId"> & {
seat: Seat;
tyre: Tyre;
};
export interface Tyre {
id: string;
diameter: number;
}
export interface Seat {
id: string;
width: number;
}
With our data structure defined, we can now create a service that provides mock data which can later be replaced with actual backend data:
@Injectable()
export class ResourcesService {
public getCars(): Observable<CarResolved[]> {
// Implementation details omitted for brevity
}
private _getCars(): Observable<Car[]> {
// Implementation details omitted for brevity
}
private _getTyreById(id: string): Observable<Tyre> {
// Implementation details omitted for brevity
}
private _getSeatById(id: string): Observable<Seat> {
// Implementation details omitted for brevity
}
}
// Mock data for demonstration purposes
const mockCars: Car[] = [
{ id: "car-1", seatId: "seat-1", tyreId: "tyre-1" },
{ id: "car-2", seatId: "seat-2", tyreId: "tyre-2" },
{ id: "car-3", seatId: "seat-1", tyreId: "tyre-3" }
];
const mockTyres: Tyre[] = [
{ id: "tyre-1", diameter: 80 },
{ id: "tyre-2", diameter: 60 },
{ id: "tyre-3", diameter: 75 }
];
const mockSeats: Seat[] = [
{ id: "seat-1", width: 10 },
{ id: "seat-2", width: 20 },
{ id: "seat-3", width: 30 }
];
The key point to note is that the `getCars` method does not involve any subscription. It simply returns an observable that can be subscribed to later on. Everything is managed through streams.
Lastly, let's look at the view setup:
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
public cars$: Observable<CarResolved[]> = this.resourcesService.getCars();
constructor(private resourcesService: ResourcesService) {}
}
Similar to the service, there are no subscriptions involved in accessing the data in the component!
Here's a preview of how the data is displayed in the HTML:
<pre>{{ cars$ | async | json }}</pre>
The updated view presents the resolved car objects with all associated details.
For a live demo showcasing these concepts, visit stackblitz: https://stackblitz.com/edit/angular-qypary