Ditching the subscribe(...) method in Angular by opting to return a string value instead of an

I have been tasked with exploring how to make a service call and return the final result instead of an observable. Within the service, there is a method structured like this:

getToken(name: string, pass: string): string {
  const url = "https://localhost:44310/api/Security/login";
  const data = { name, pass };

  return this.http.post<any>(url, data);
}

In the component where it will be used, we typically subscribe to it and handle the result that comes back (or handle any errors that occur). Now, I am looking to implement a private field that will be checked when calling getToken(...) and returned if it has already been assigned. If not, then I want to make a POST request, store the result, and then return it.

private token: string;

getToken(name: string, pass: string): string {
  if(this.token)
    return this.token;

  const url = "https://localhost:44310/api/Security/login";
  const data = { name, pass };

  this.http.post<any>(url, data)
    .subscribe(
      suc => this.token = suc,
      err => this.token = null);

  return this.token;
}

However, this approach will fail because the asynchronously assigned value to this.token may not be available by the time the method finishes. Trying to return the token within the success callback like this:

this.http.post<any>(url, data)
  .subscribe(
    suc => { this.token = suc; return this.token; },
    err => this.token = null);

is unlikely to work any better. I am currently stuck and starting to think that there may not be a way to return a definitive value without using the subscription in the component.

Is there a way to implement the described field as intended?

I have attempted to follow this post but it resulted in an error: Property 'map' does not exist on type 'Observable'.

Answer №1

It's important to keep the observable in the call stack. There is no reason to remove it. Observables are highly beneficial in an async call stack scenario, which is what you have here.

It seems like the issue you're facing involves avoiding duplicate calls by caching the result. This can be easily achieved.

private token: BehaviorSubject<string|null> = new BehaviorSubject<string|null>(null);
private token$ = this.token
  .asObservable()
  .pipe(filter((value) => value != null));

getToken(name: string, pass: string): observable<string> {

  const url = "https://localhost:44310/api/Security/login";
  const data = { name, pass };

  if (this.token.value == null) {
    this.http.post<any>(url, data)
      .subscribe(
        value => this.token.next(value),
        _ => this.token.next(null));
  }

  return this.token$;
}

Example for subscribing:


  someObject.getToken(params)
    .subscribe(token => {
      // When the token is available, it will not be null.
      // Since the source is a BehaviorSubject, we get the last value
    }); 

Note: Remember to unsubscribe if you are not using UI Pipe Async.

Reference to Subject:

I am wondering if there is a way to encapsulate this and return a simple string (if it's already assigned) or (if the string hasn't been stored yet) return the async value after some time while keeping the executing method in the component locked.

Technically, this could be done, but it is not a good practice:

private token: string | null = null;
getToken(name: string, pass: string): string | observable<string>{

  if (this.token != null) {
    return token;
  }

  const url = "https://localhost:44310/api/Security/login";
  const data = { name, pass };

  return this.http.post<any>(url, data)
      .tap(value => this.token = value);
}

By providing either a string or observable<string>, you are meeting your request. However, this approach creates unnecessary complexity for consumers and would confuse anyone reading the code.

Subscriber example:


   var result = someobject.getToken(params);
   if (result typeof 'string') {
     dosomethingwithstring(result);
   }
   else {
     result.subscribe(result2 => dosomethingwithstring(result));
   }

Leaving other programmers with this pattern would be difficult to maintain and not recommended.

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

How to correct placeholder text display in the input of a Material UI text field

Currently, I am utilizing the material ui text field component. The issue at hand is that when the text field is in focus, the placeholder shifts to the top of the field. https://i.stack.imgur.com/P5flf.png I prefer for the placeholder to remain within ...

Exploring the process of mapping addresses with parameters in the proxy configuration file of Angular

Currently, as I develop my application, I am utilizing a proxy-config.json file to direct my API calls to the backend server operating on a separate port. I am facing an issue when attempting to call an address with a parameter. I am unsure of how to handl ...

Monitor changes in Angular Reactive forms to retrieve the field name, as well as the previous value and the current value of the field

this.formData = this.fb.group({ field1: '', field2: '' }); this.formData.valueChanges.pipe(startWith(null), pairwise()) .subscribe(([previous, current]: [any, any]) => { console.log('PREVIOUS', previous); co ...

Steps to modify the CSS of a custom component in Angular 8

I have been attempting to override the css of a custom component selector, however, my attempts have been unsuccessful. I have tried using ":ng-deep" but it hasn't worked. How can I go about finding a solution for this issue? app.component.html: < ...

What strategies can be employed to mitigate the activation of the losing arm in a Promise.race?

My current task involves sending the same query to multiple identical endpoints (about five) across various Kubernetes clusters. The goal is to aggregate the results without any delays and report failures to the user while continuing with the process seaml ...

Angular: bypassSecurityTrustHtml sanitizer does not render the attribute (click)

I'm facing an issue where a button I rendered is not executing the alertWindow function when clicked. Can anyone help?: app.component.ts: import { Component, ElementRef, OnInit, ViewEncapsulation } from '@angular/core'; import ...

Identified the category

How can I retrieve the default option from a list of options? type export type Unpacked<T> = T extends Array<infer U> ? U : T; interface getDefaultValue?: <T extends Option[]>(options: T) => Unpacked<T>; Example const options = ...

Discovering the specific p-checkbox in Angular where an event takes place

Is there a way to assign an ID to my checkbox element and retrieve it in my .ts file when the checkbox is selected? I want to pass this ID along with the true or false value that indicates if the checkbox is checked. Here's an example code snippet: &l ...

Different methods to prompt TypeScript to deduce the type

Consider the following code snippet: function Foo(num: number) { switch (num) { case 0: return { type: "Quz", str: 'string', } as const; case 1: return { type: "Bar", 1: 'value' } as const; default: thr ...

Enhance Accordion Component with New Information

I am in the process of updating an accordion based on search results returned as an array. Initially, the accordion displays the full set of results from a service, but when I perform a search, the console shows the correct values, however, the ngModel i ...

Removing properties of an object or a mapped type in Typescript by their values

Can we exclude specific properties from an object using a mapped type based on their value? Similar to the Omit function, but focusing on the values rather than the keys. Let's consider the following example: type Q = {a: number, b: never} Is there ...

What is the process for utilizing global packages when running `ng serve`?

After successfully using npm to download and install packages globally for my project, I encountered an error when running ng serve --port 34000: C:\app>ng serve --port 34000 Node packages may not be installed. Try installing with 'npm install ...

Why is the `node-config` configuration undefined within a TypeScript Jest environment?

I have a TypeScript module that is functional in both development and production environments. It utilizes https://github.com/lorenwest/node-config. When I attempt to import it into Jest for testing purposes, I encounter an error suggesting that the config ...

What is the best way to update the mat-tab when the routeParameters are modified?

I need to reinitialize the mat-tab-group in order to make the first tab active when there is a change in the routeParams. ts file: public index = 0; ngOnInit() { this.subscription = this.route.params.subscribe((routeParams: Params) => { // some ...

The timestamps I generate are based on the day following the date

While creating a schema and using {timestamps:true} in Mongo, the fields 'createdAt' and 'updateAt' are supposed to be automatically generated. However, I have noticed that when creating a document with this setup, the day of the date i ...

No types are assigned to any props

I recently began working on a SvelteKit skeleton project for my personal website. However, I encountered an error when using Svelte with TypeScript - specifically, I kept getting the message Type '<some prop type>' is not assignable to type ...

Inject the data within Observable<Object> into Observable<Array>

I'm faced with a situation where I have two distinct API endpoints. One endpoint returns a single Card object, while the other endpoint returns an Array of Card objects. My goal is to retrieve the first Card from the single Card endpoint and place it ...

What is the process for creating a new element and utilizing its reference to add child elements in React?

I've been struggling to create an HTML element in a parent component in React, and then access that component's div from a child component in order to add new elements to it. Despite multiple attempts, I can't seem to resolve the issue of p ...

The issue of Angular child components rendering before receiving parent data

My current challenge involves a parent component (app.component) making an http request, storing the result in this.stats and then passing it as a prop to the child component (progression.component). The issue arises when my child component tries to render ...

Enhancing an existing Angular1 project to Angular4 while incorporating CSS, Bootstrap, and HTML enhancements

Facing CSS Issues in AngularJs to Angular4 MigrationCurrently in the process of upgrading an AngularJS project to Angular 4, specifically focusing on updating views. However, encountering issues with certain views not displaying the custom CSS from the the ...