Issue with subscribing to nested observables, unable to successfully unsubscribe

My app is using Firebase auth with Firestore (https://github.com/angular/angularfire2). Despite my efforts to unsubscribe from all observables fetched from Firestore before signing out, I keep encountering a "FirebaseError: Missing or insufficient permissions" issue. This error seems to stem from a nested subscription within another subscription in the code snippet below.

Even after unsubscribing from both subscriptions in the ngOnDestroy method, the error persists.

this.proposalSubscription = this._posts.retrieveProposals(user.uid)
  .subscribe(proposals => {
      this.proposals = proposals;
      this.proposals.forEach(proposal => {
        this.chatSubscription = this._chat.retrieveChat(proposal.id).subscribe(chat => {
          if (chat) {
            this.chats.set(proposal.id, chat)
          }
        });
      })
    });

I suspect that the problem lies in this line of code:

this.chatSubscription = this._chat.retrieveChat(proposal.id).subscribe(chat => ...
Even though I ensure to unsubscribe from both proposalSubscription and chatSubscription before signing out, the error persists. Can anyone suggest a solution to this issue? Additionally, I have limited experience with rxjs and its operators. Is there an operator that can help avoid such nested subscriptions?

Any insights would be greatly appreciated. Thank you.

Answer №1

To establish your subscription, follow these steps:

this.subscriptionModel = this._posts.requestSubscriptions(user.uid)
                                    .pipe(
                                      switchMap(subscriptions => {

                                        // Save subscriptions in a class variable like this
                                        this.subscriptions = subscriptions;

                                        const obs$ = subscriptions.map(s => {
                                          return this._chat.getChat(s.id)
                                                     .pipe(
                                                       map(chat => {
                                                         this.conversations.set(s.id, chat);
                                                       })
                                                     )
                                        });

                                        return forkJoin(obs$);
                                      })
                                    )
                                    .subscribe();

You have the flexibility to arrange your operator chain as desired and maintain only one subscription.

In ngOnDestory, remember to unsubscribe from the subscription:

ngOnDestroy() {

 if(this.subscriptionModel) {
   this.subscriptionModel.unsubscribe()
 }
}

If you anticipate evaluating the

this._posts.requestSubscriptions(user.uid)
observable only once, you can eliminate the explicit unsubscribe in ngOnDestroy by using take(1) like this:

this._posts.requestSubscriptions(user.uid)
                                    .pipe(
                                      take(1),
                                      switchMap(subscriptions => {

                                        // Save subscriptions in a class variable like this
                                        this.subscriptions = subscriptions;

                                        const obs$ = subscriptions.map(s => {
                                          return this._chat.getChat(s.id)
                                                     .pipe(
                                                       map(chat => {
                                                         this.conversations.set(s.id, chat);
                                                       })
                                                     )
                                        });

                                        return forkJoin(obs$);
                                      }),                                          
                                    )
                                    .subscribe();

Answer №2

When it comes to managing dependent observables, using switchMap instead of nested subscriptions is a more efficient approach. To handle multiple subscriptions within the same component, employing takeUntil allows you to emit a single value into a subject and cancel all subscriptions simultaneously. By mapping proposals to an array of observables and utilizing combineLatest, you can obtain an array containing the results of those observables.

finalise = new Subject();

this._posts.retrieveProposals(user.uid)
  .pipe(
    switchMap(proposals => combineLatest(proposals.map(proposal => this._chat.retrieveChat(proposal.id)))),
    takeUntil(finalise)
  ).subscribe(chats => {
    const chat = proposals.find(p => p);
    this.chats.set(proposal.id, chat)
  });

In the ngOnDestroy method:

this.finalise.next();

This will effectively terminate all subscriptions linked to finalise in the takeUntil operator.

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

Error encountered while implementing onMutate function in React Query for Optimistic Updates

export const usePostApi = () => useMutation(['key'], (data: FormData) => api.postFilesImages({ requestBody: data })); Query Definition const { mutateAsync } = usePostApi(); const {data} = await mutateAsync(formData, { onMutate: ...

Typescript validation of tokens using Azure functions

Currently working on a website utilizing Azure Static Web App, where the login/registration is managed by Azure B2C. The backend API consists of typescript Azure functions integrated with Azure Static web app. Certain API calls can only be accessed when th ...

What circumstances warrant the utilization of an Angular service?

My understanding is that services are utilized for inter and intra component communication to abstract complex data structures. Is it accurate to say that services are exclusively used for persistent data structures? In what scenarios should we avoid usi ...

Recording the details of an Angular project through the utilization of Compodoc

I am currently in the process of documenting my Angular + Typescript application using Compodoc. To install Compodoc, I utilized npm and executed the following command: 'npm install -g compodoc'. And included "compodoc": "./node_modules/ ...

How can ng-content be used to adjust the displayed content in Angular?

I am working with two components: hostComponent and textComponent. My goal is to project content inside textContent and make modifications based on other input properties. <app-text-component characterCount='5'> <span> Hello World ...

Utilize Ngrx to keep an eye on specific items within the store

If we consider an interface called INotification: export interface INotification { id: number; DateReceived: number; Title: string; Message: string; Tipology: string; isRead: number; } and a reducer system. In the component, it&ap ...

Accommodate the Angular form with a null value

In my form initialization method, I am encountering an issue when there is no email value coming from the API. This results in the error message: ERROR TypeError: Cannot read property 'value' of undefined private initForm() { this._userSer ...

Ways to limit file access and downloads on an IIS server

After deploying our Angular app (dist folder) on an IIS server, everything seems to be working well. However, there is a concerning issue where anyone can access and download the font files directly from the server without needing to log in. For example, o ...

What is preventing TypeScript from automatically inferring the type of an argument in a generic function that utilizes `keyof`?

What causes the error in this code snippet? type MyType = { a: string, b: string } function cantInfer<In, Out>(fn: (i: In) => Out, i: In) { } function myFunction<K extends keyof MyType>(key: K): string { return ''; } ...

Creating a sidebar in Jupyter Lab for enhanced development features

Hi there! Recently, I've been working on putting together a custom sidebar. During my research, I stumbled upon the code snippet below which supposedly helps in creating a simple sidebar. Unfortunately, I am facing some issues with it and would greatl ...

Broaden the natural interface for the element

I'm looking to create a uniquely customized button in React using TypeScript. Essentially, I want to build upon the existing properties of the <button> tag. Below is a simplified version of what I have so far: export default class Button extend ...

Issue with Adding Class to Angular2 Material Tooltips

Here is the code from my component.html file: <tr> <td> <span matTooltipClass="primary-tooltip" matTooltipPosition="above" matTooltipHideDelay="100000" matTooltip="{{cert.awsCertId}}"><p style="overflow:hidden;text-overflow: ellip ...

What is the best way to modify the underline style of a tab in Material UI?

I'm trying to customize the underline of: https://i.stack.imgur.com/J2R1z.png Currently, I am using material ui version 4.12.3 The code snippet for generating my tabs is below: function renderTabs(): JSX.Element { return ( <Tabs className={cla ...

Approach to Monitoring Notifications

Is there a best practice for managing notifications in an AngularJS application? When I mention 'notifications', I am referring to alerts that should be displayed to the user while they are logged into the app. My idea is to show the user any u ...

What are the repercussions of labeling a function, TypeScript interface, or TypeScript type with export but never actually importing it? Is this considered poor practice or is there a potential consequence?

I find myself grappling with a seemingly straightforward question that surprisingly has not been asked before by others. I am currently immersed in a TypeScript project involving Vue, and one of the developers has taken to labeling numerous interfaces and ...

API rest data retrieval service in Angular

I'm currently working on my first Angular App and I need to fetch data from another local API Rest. My project is inspired by the Angular tutorial tour-of-heroes. I have created a component service to make API calls and display the results in another ...

Eliminate the loading screen in Ionic 2

Within my app, there is a button that triggers the opening of WhatsApp and sends a sound. Attached to this button is a method that creates an Ionic loading component when clicked. The issue I am facing lies with the "loading.dismiss()" function. I want the ...

Disable the default animation

Is there a way to disable the default animation of the Select label in my code snippet below? export default function TicketProfile(props: any) { return ( <Container> <FormControl sx={{ ml: 1, mr: 1, minWidth: 220 }}> <Inp ...

URL for image preview on Amazon S3

Is there a way to retrieve preview images from my Amazon S3 image storage instead of always fetching the full-sized 5MB images? If necessary, I would then be able to request the normal image. ...

The type 'TaskListProps[]' cannot be assigned to type 'TaskListProps'

I'm struggling with handling types in my TypeScript application, especially with the TaskListProps interface. export default interface TaskListProps { tasks: [ { list_id: string; title: string; description: string; status ...