How to link observables in HTTP requests using Angular 5?

I'm currently developing an application using Angular 5, and I want to segregate raw http calls into their own services so that other services can modify the responses as needed. This involves having a component, component service, and component data service.

My question is how can I chain observables following this design pattern? Below is my attempted code:

licenseData.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http';
import { CONSTANT } from '../../environment/constants';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';

@Injectable()
export class LicenseDataService {

    constructor(private http: HttpClient) {}

    getLicenseInfo() {

        const params = new HttpParams()
            .set('unsername', 'swapp')
            .set('comment', "hi");

        const endpoint = '/myendpoint';

        return this.http
            .get(endpoint, {params: params})
            .map((response) => {
                console.log(' response ', response);
                //return Observable.of<any>(response);
                return response['data'];
            })
            .catch(this.errorHandler);
    }

    errorHandler(error: HttpErrorResponse) {
        //TODO: log the error here
        return Observable.throw(error);
    }

}

license.service.ts

import { Injectable } from '@angular/core';
//import { Observable } from 'rxjs/Observable';
import { LicenseDataService } from './licenseData.service';

@Injectable()
export class LicenseService {

    constructor(private licenseDataService: LicenseDataService) { }

    getVersion() {

        //handle the error in here and the loading as well

        return this.licenseDataService.getLicenseInfo()
            .subscribe((response) => {
                return response['versionNumberField'];
            });
        //console.log(' result ', result);

        //return result['versionNumberField'];
    }

}

and here is part of my component code:

export class AboutComponent implements OnInit {

    //version: Observable<string>;
    version: string;

    constructor(private licenseService: LicenseService) {
        console.log('Hello Component');
    }

    ngOnInit() {
        console.log('ngOnInit');

        //this.version = this.licenseService.getVersion();

        this.licenseService.getVersion()
           .subscribe(response => this.version = response);
    }

}

My project won't build. How can I properly chain these components?

Answer №1

It is not possible to subscribe to a Subscription object.

In your LicenseDataService file, you have the following code:

return this.licenseDataService.getLicenseInfo()
            .subscribe((response) => {
                return response['versionNumberField'];
            });

Then in your LicenseService, you are trying to Subscribe to the result of what LicenseDataService returns, which is already a Subscription. One solution is to use map in LicenseService and then Subscribe in the Component.

return this.licenseDataService.getLicenseInfo()
            .map((response) => {
                return response['versionNumberField'];
            });

After making this change, your subscribe in the Component should work properly.

Personally, I would recommend sticking with just one service as these operations are not overly complex.

Answer №2

Avoid managing subscriptions within your service unless it's absolutely necessary.

If I were in your shoes, I would expose the HTTP service as an observable and utilize share replay to maintain shared results when subscribing to the appVersion throughout the app's lifecycle. This approach eliminates internal subscriptions within services, which typically indicate problematic code.

    @Injectable()
    export class LicenseService {


          private _appVersion$;

          get appVersion$(){
              if(!this_appVersion$){
                this._appVersion$ = this.getVersion().pipe(shareReplay());   
              }
              return this._appVersion$;
          }

          private getVersion() {

        //handle the error in here and the loading as well

            return this.licenseDataService.getLicenseInfo()
               .pipe(
                  map(response => return response['versionNumberField'] as any
               );
         }     
   } 

In your component, inject the service and subscribe to the appVersion$ observable to access the app version value.

This method follows a more reactive programming paradigm, enabling you to chain actions with other asynchronous code without embedding subscriptions within a service.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I encountered an error in my Node.js application stating that it could not find the name 'Userdetailshistory' array. I am puzzled as to why this error is occurring and I suspect it may be due to my

import { Component, OnInit } from '@angular/core'; import { UserdetailshistoryService } from '../../services'; @Component({ selector: 'my-userdetailshistory', templateUrl: './userdetails-history.component.html', ...

A collection of JSON data containing various diverse values

My classes are not specific and they look like this: type SyncReducerAction<TState> = (state: TState, ...args: any[]) => TState; type AsyncReducerAction<TState, TResult, TRest extends any[]> = { promise: (...args: TRest) => Promise< ...

The Ng4LoadingSpinner will automatically stop after 5 seconds

When utilizing Ng4LoadingSpinner, I encountered an issue where it disappears automatically after the default timeout of 5 seconds, even though our request is still processing. Increasing the timeout resolves the problem, but I am seeking an alternative s ...

Using Ionic 4 and Angular 7 to make straightforward remote HTTP requests with Cross-Origin Resource

I'm still navigating my way through ionic and angular, trying to understand how to send a basic HTTP request to a remote server without dealing with CORS. My initial attempt involved the following code snippet: this.httpClient.get<MyObj>("/api" ...

Updating the Angular2 function in the main app component causes the current component to be reset

I developed an application that has the following structure: app.component.ts import { Component } from 'angular2/core'; import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router'; import { NgClass } from &apos ...

Tips for setting ngModel and name attributes in an angular test for a custom component

Just getting started with angular. I recently developed a custom component that implements the ControlValueAccessor to allow developers to easily access its value. Here's an example of how it can be used: <app-date [label]="'Date 2&apos ...

Best practices for updating the value of a specific key within an object that contains recursion in JavaScript/TypeScript

I have a tree component that uses the following data structure type TreeNode = { id: string, parentId: string, renderer: () => React.ReactNode, expanded: boolean, children?: Array<TreeNode>, } Now, I am looking to add functionality for ...

Why won't my Angular app hit a breakpoint while debugging?

I'm relatively new to the world of Visual Studio Code and Angular applications with a C# Web API back-end. My issue lies in hitting breakpoints within my Angular app using VS Code, even though I can hit them without any problems in C#! Running both a ...

My HTML files are not recognizing the IONIC Property within their own objects

As I delve deeper into understanding Angular and Ionic, a peculiar issue has arisen for which I seek a solution. I have several export classes containing HTML forms. In each corresponding .ts file, I declare a variable and import the relevant model to bin ...

Using TypeScript, a parameter is required only if another parameter is passed, and this rule applies multiple

I'm working on a concept of a distributed union type where passing one key makes other keys required. interface BaseArgs { title: string } interface FuncPagerArgs { enablePager: true limit: number count: number } type FuncArgs = (Fu ...

Tips for retrieving a child component's content children in Angular 2

Having an issue with Angular 2. The Main component displays the menu, and it has a child component called Tabs. This Tabs component dynamically adds Tab components when menu items are clicked in the Main component. Using @ContentChildren in the Tabs comp ...

How can we automate the process of assigning the hash(#) in Angular?

Is it possible to automatically assign a unique hash(#) to elements inside an ngFor loop? <div *ngFor="let item of itemsArray; index as i"> <h3 #[item][i]> {{ item }} </h3> </div> I would like the outp ...

ERROR TS1086: A declaration of an accessor within an ambient context is not allowed. Accessor for firebaseUiConfig(): NativeFirebaseUIAuthConfig

Trying to create a Single Page Application with Angular/CLI 8. Everything was running smoothly locally until I tried to add Firebase authentication to the app. Upon compiling through Visual Studio Code, I encountered the following error message: ERROR in ...

Steps to stop mat-spinner upon receiving Job Success/Failure Notification from the backend

I have a task that runs asynchronously and takes a long time to complete. When the task starts, I display a mat-spinner with a timeout set at 60000 milliseconds. However, we now have a notification service that provides updates on the job status. I would l ...

Exiting a void method in JavaScript/Typescript using the return or break statement

I find myself dealing with a complex method in Typescript that contains multiple if else if else constructs. It's a void method, and I'm wondering how I can exit the method based on a specific if condition without having to execute the remaining ...

Angular2 - leveraging root-relative imports

Having trouble with imports in angular2/typescript? Want to use paths relative to the project root like 'app/components/calendar', but currently stuck using something like this: //app/views/order/order-view.ts import {Calendar} from '../../ ...

Best practice for importing ts files from an npm package

When faced with the need to divide a ts project into multiple repositories/packages for creating microservices, the challenge arises in combining these packages efficiently. Some packages are required in one microservice, others in another, and some in all ...

What is the correct way to test setInterval() statements within Angular?

Here is a simple code snippet I am working on: public async authenticate(username: string, password: string) { const authenticationResponse = await this.dataProvider.authenticate(username, password); if (authenticationResponse.result.code == 0) { ...

Angular: ensure the form reverts to its initial value when the modal is closed and reopened

I am facing an issue with my items section. When I click on an item, a modal window opens allowing me to edit the text inside a textarea. However, if I make changes to the text and then cancel or close the modal, upon reopening it, the previously modified ...

ngStyle isn't being correctly implemented

I've encountered an issue in my Angular 5 application where [ngStyle] is not translating to the style attribute as expected. Instead, I only see ng-reflect-ng-style in the generated HTML. This functionality was working fine before. Could there have be ...