share an rxjs Subject between multiple functions

My ApiClientService takes an rxjs Service as a parameter in all its methods to call APIs, subscribe to returned Observables, parse values, and then callback the Subject's next().

However, in the UserService class, a single Subject used for multiple API calls does not trigger the second API's return. The API returns correctly, and Service.next() is called with the expected value.

I'm wondering if this behavior (one service per Observable) is intentional or if there is a design issue.

Thank you!

// ApiClientService.ts

 @Injectable()
 export class ApiClientService {
   postUserLogin(userid: string, password: string): Observable<Object> {
     const url = "/api/getforuser";
     const httpOptions = {
       headers: new HttpHeaders({
         'Content-Type': 'application/json',
       })
     }
     return this.http.post(url, { userid: userid }, httpOptions);
   }

   postSessionLogin(sessionid: string): Observable<Object> {
     const url = "/api/getforsession";
     const httpOptions = {
       headers: new HttpHeaders({
         'Content-Type': 'application/json',
       })
     }
     return this.http.post(url, { sessionid: sessionid }, httpOptions);
   }
 }

UserService.ts

// UserService.ts

@Injectable()
export class UserService {

  currentUser: UserModel;

  constructor(
    private apiclient: ApiClientService
  ) { }

  isLoggedIn(): boolean {
      if ( this.currentUser == null ) {
          return false;
      } else if ( this.currentUser.session != null ) {
          if ( this.currentUser.session.length > 0 ) {
              return true;
          }
      }
      return false;
  }

  sessionLogin(userListener: Subject<UserModel>) {
    console.log("UserService.sessionLogin");
    var session = this.cookieService.get("session");
    if ( session == null ) {
      userListener.error("session not found");
    } else {
      var obRes = this.apiclient.postSessionLogin(session);
      obRes.subscribe( res => {
        console.log("sessionLogin response");
        var payload = res['payload'];
        console.log("payload: " + payload);
        var user = payload['user'];
        console.log("user: " + user);
        this.currentUser = new UserModel;
        this.currentUser.userid = user.userid;
        userListener.next(this.currentUser);
      });
    }
  }

  userLogin(username: string, password: string, userListener: Subject<UserModel>) {
    console.log("UserService.userLogin start");
    var obRes = this.apiclient.postUserLogin(username, password);
    obRes.subscribe( res => {
      console.log("UserService.userLogin response start...");
      console.log(res);
      console.log("userLogin response json...");
      var payload = res['payload'];
      console.log("payload: " + payload);
      var user = payload['user'];
      console.log("UserService.userLogin user: " + user);
      this.currentUser = new UserModel;
      this.currentUser.userid = user.userid;
      userListener.next(this.currentUser);
    }, err => {
      console.log("not even error, nothing...");
      console.log(err);
    });
  }

}

The frontend component using UserService

// UserLoginComponent
@Component({
  selector: 'app-home-login',
  templateUrl: './home-login.component.html',
  styleUrls: ['./home-login.component.scss']
})
export class HomeLoginComponent implements OnInit {

    @Input() userModel: UserModel;

    loggedinUser: UserModel;

    userloginForm: FormGroup;

    loginListener: Subject<UserModel>;
    loginListenerSubs: Subscription;

    constructor(private fb: FormBuilder,
      private router: Router,
      private userService: UserService
    ) {
        this.createForm();
    }

    createForm() {
        this.userloginForm = this.fb.group({
            username: [ '', Validators.required ],
            password: [ '', Validators.required ]
        });
    }

    onSubmitLogin() {
        this.userModel = this.pullFormContent();
        this.userService.userLogin(this.userModel.username, this.userModel.password, this.loginListener);
    }

    pullFormContent() {
        const formModel = this.userloginForm.value;
        // Form content processing logic here

        const user: UserModel = {
          userid: 0,
          username: formModel.username,
          password: formModel.password,
        }
        return user;
    }

    onLoginSuccess(user) {
      // Login success handling
    }
    onLoginFailed(error) {
      // Login failure handling
    }
    onLoginCompleted() {
      // Post login completion logic
    }

    ngOnInit() {
      // Component initialization logic
    }

    ngOnDestroy() {
      // Component cleanup logic
      this.loginListenerSubs.unsubscribe();
    }

}

Answer №1

I cannot determine the exact cause of the issue, but I can offer some suggestions that may help resolve it.

Why Utilize Subject?

Instead of returning the Observable from the HTTP client directly to your component, consider using the map operator for transformation. For example:

sessionLogin() {
    console.log("UserService.sessionLogin");
    var session = this.cookieService.get("session");
    if ( session == null ) {
      Observable.throw("session not found");
    } else {
      return this.apiclient.postSessionLogin(session)
      .map( res => {
        console.log("sessionLogin response");
        return this.buildUser(res);
      });
    }
  }

userLogin(username: string, password: string) {
    console.log("UserService.userLogin start");
    return this.apiclient.postUserLogin(username, password)
    .map( res => {
      console.log("UserService.userLogin response start...");
      console.log(res);
      console.log("userLogin response json...");
      return this.buildUser(res);
    };
  }

buildUser(res) {
      const payload = res['payload'];
      console.log("payload: " + payload);
      const user = payload['user'];
      console.log("UserService.userLogin user: " + user);
      this.currentUser = new UserModel;
      this.currentUser.userid = user.userid;
      return this.currentUser;
}

Afterward, you can subscribe to the Observable returned by either the sessionLogin or userLogin method.

Automated Logging in ngOnInit()

In your ngOnInit() method, there is a section focused on automatic login:

// try to login automatically
  this.userService.sessionLogin(this.loginListener);

  if ( this.userService.isLoggedIn() ) {
        console.log("user is logged in, todo: auto redirect");
        this.router.navigate(['/some/user/view']);
  }

It's important to note that calling sessionLogin and immediately checking the user's login status with isLoggedIn may lead to confusion. If the user is already logged in, there may be no need to call

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

Which superclass does ReadonlyArray extend from?

Looking at this TypeScript code snippet: const arr: ReadonlyArray<string> = [ "123", "234" ]; arr.push("345"); An error is thrown by the TS compiler: Property 'push' does not exist on type 'ReadonlyArray<string>&apo ...

What is the best way to inform TypeScript that it is acceptable to reference an export that may or may not exist in the current revision of a module?

Using React's Profiler in Production is my aim, but the version of React I am currently using is React 16.5, which exports the unstable_Profiler. The team responsible for delivering an optimized asset bundle (including React) will be updating to Reac ...

Adding up elements in a vector using TypeScript

Is there a way to calculate the sum of values based on the name without repetition in "dataGrf"? I've tried various methods but am facing difficulties. Here is a brief example below to help illustrate what I'm attempting to achieve. Note: Please ...

Transferring an Angular 2 component with output parameters to Angular 1 via Downgrade

After successfully downgrading an Angular 7 component to Angular 1, I encountered a small issue that has proven difficult to resolve. The output parameter in my downgraded component is defined as: @Output()isValid = new EventEmitter<boolean>(); An ...

Is there a way to end my session in nextAuth on NextJS 14?

Recently, I've started using Typscript with a NextJS14 project that utilizes NextAuth for authentication. While there weren't any errors in the JavaScript version of my code, I encountered an error when working with TypeScript. This is a snippet ...

Is there a problem with Angular2 using TypeScript?

Currently, I am in the process of setting up my machine for Angular development by following the guidelines provided on https://angular.io/docs/ts/latest/quickstart.html As I proceeded to run "npm start" to launch my site, I encountered an issue with the ...

Tips for integrating a new module into the SystemJs configuration file for seamless importing in Angular

Is there a way to incorporate newly downloaded packages from npm into my Angular 2 components using SystemJS and the system.config.js file provided by a starter package? I've attempted to include links to the modules in the map and packages sections o ...

predefined values for interfaces paired with universal functions

In the development of my project, I am creating a type system centered around commands and their corresponding results. My main objective is to build commands and eventually serialize them into JSON for transmission over the network. Since there will be nu ...

Assessing the validity of a boolean condition as either true or false while iterating through a for loop

I am facing an issue that involves converting a boolean value to true or false if a string contains the word "unlimited". Additionally, I am adding a subscription to a set of values and need to use *NgIf to control page rendering based on this boolean. &l ...

Are there any methods to incorporate Facebook and Google login into an Ionic progressive web app (PWA)?

After successfully developing an app in Ionic 3 for Android and iOS, I encountered a problem when adding the browser platform. The Facebook and Google login features were not functioning as expected. Despite the assurance from Ionic documentation that the ...

Insight on the process of submitting interactive forms

I'm facing a challenge that I can't seem to figure out It's a form structured in HTML like this: <button (click)="addform()">add form</button> <div class="conten-form"> <div class="MyForm" ...

Is there a way to automatically establish a connection with a BLE device when it is within

I am currently working on an app for IONIC 2 that requires my BLE device to automatically connect when in range. The app should be able to handle this whether it is in the background or foreground, and if the connection is lost, it should continuously se ...

Converting Vue 3 refs to different types using instanceof in Typescript

When working with Vue using the Options API, here is a code snippet I have: <script lang="ts"> import * as THREE from 'three'; export default { mounted() { console.assert(this.$refs.container instanceof HTMLCanvasElem ...

Is it feasible for the Drawer to be a fixed feature that sits atop the content?

I am looking to have a compact drawer positioned on the left side of my screen, similar to the example shown in the Material UI Documentation: https://i.sstatic.net/W21Kd.png However, I would like it to float over the content (like the variant="temporary ...

Component re-rendering and initializing useReducer

I made some revisions to this post. Initially, I shared the entire problem with my architecture and later updated it to focus directly on the issue at hand in order to make it easier for the community to provide assistance. You can now jump straight to the ...

Error: The 'itemsNav' property is not part of the '{}' type. (Code: 2339)

I'm struggling to figure out the issue with itemsNav. <script setup lang="ts"> import { ref } from 'vue' import { list } from '../../constants/NAV'; const itemsNav = ref(list) </script> < ...

Avoiding deadlock when calling an asynchronous function within a for loop in TypeScript and NHibernate

When writing an http post call inside a for loop with TypeScript, I encountered an issue while debugging the backend method. It seems that the requests are being handled simultaneously. For instance, if the server needs to execute 2 methods M1() and then ...

Is there a way to link two observables together and then access the inner observable for subscription?

I have a function that is not working as expected. Although I believe it is close to the correct solution, something seems to be missing. deactivateSegment(id, isUsingName?){ if(isUsingName) { return this.getSpecificSegment(id).pipe(concatMap(( ...

The element reference does not have a property called 'registerComponent' associated with it

I encountered a problem while working on golden-layout with angular2. I followed this plunker link, but I ended up facing the following errors: The issue 'Property 'registerComponent' does not exist on type 'ElementRef'. The erro ...

Creating a list of components for drag and drop with Angular CDK is a straightforward process that involves following

I am attempting to use Angular's CDK drag and drop functionality to create a list of components that can be rearranged. However, I am encountering an issue where the components are not being displayed correctly. In my App.component.ts file: impo ...