Transferring Information Across Angular Components via a Service Utilizing an RxJS Subject

The Goal I'm Pursuing

In my Angular client app, I have two separate components. One component handles data fetching and processing from the server. My objective is to utilize the subscribe callback function to navigate to a different page where I can display processed data in the form of a blob. To be more precise, I aim to open a new tab and provide a preview of a word document using an unrelated component. Through research, I discovered that passing data between unrelated components can be achieved by utilizing a service with an RxJS subject.

The Current Outcome

Although I have written some code, I am facing an issue where the blob does not reach the preview component as intended.

Attempts Made So Far

Below is the code I have devised:

Service for sharing a Blob object

@Injectable()
export class DocPreviewService {
  private blob = new BehaviorSubject<Blob | undefined>(undefined);
  public share$ = this.blob.asObservable();

  constructor() {}

  setBlob(blob: Blob) {
    this.blob.next(blob);
  }
}

Function responsible for retrieving the blob from the server (first component)

  showReport(selected_id: string) {
    const url = this.router.serializeUrl(this.router.createUrlTree([`tools/${selected_id}/preview`]));
    // Opening the page in a new tab is crucial
    window.open(url, '_blank');

    this.report_service.createReport(this.calc_items[selected_id]).subscribe({
      next: (doc_blob: Blob) => {
        {          
          this.doc_preview_service.setBlob(doc_blob);
        }
      },
      error: (error: any) => {
      },
    })
  }

Component for viewing a document (second component)

export class DocPreviewComponent implements OnChanges, AfterViewInit, OnDestroy {
  doc_blob?: Blob;
  @ViewChild('doc_preview') doc_preview!: ElementRef;
  subscription: Subscription;
  
  constructor(private doc_preview_service: DocPreviewService, private local_storage: LocalStorageService) {
    this.subscription = this.doc_preview_service.share$.subscribe(blob => {
      this.doc_blob = blob;
    });
  }

  ngOnChanges(changes: SimpleChanges): void {}

  ngAfterViewInit(): void {
    if (this.doc_blob) {
      doc.renderAsync(this.doc_blob, this.doc_preview.nativeElement)
      .then(x => console.log("docx: finished"));
    }
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

I would greatly appreciate any assistance provided. Thank you.

Answer №1

It's worth noting that the subject emitter method comes with a limitation - it only facilitates communication between unrelated components within the same tab. Cross-tab communication is not achievable due to each tab having separate service instances, resulting in events not being received.

To overcome this obstacle, one simple solution is to append the API call parameters to the URL.

Here is an example of code for opening a new tab:

  showReport(selected_id: string) {
    // By appending the required params for the API call to the URL
    // we can utilize it for blob logic upon initialization of the new tab!
    const url = this.router.serializeUrl(this.router.createUrlTree([`tools/${selected_id}/preview?id=${this.calc_items[selected_id]}`]));
    // Ensure the new page opens in a new tab
    window.open(url, '_blank');
  }

The component responsible for opening the blob will look like this:

export class DocPreviewComponent implements OnChanges, AfterViewInit, OnDestroy {
  doc_blob?: Blob;
  @ViewChild('doc_preview') doc_preview!: ElementRef;
  subscription: Subscription;
  
  constructor(private doc_preview_service: DocPreviewService, private local_storage: LocalStorageService) { }

  ngOnChanges(changes: SimpleChanges): void {}

  ngAfterViewInit(): void {
      this.report_service.createReport(this.activatedRoute.snapshot.queryParams.id).subscribe({
         next: (doc_blob: Blob) => {
              this.doc_blob = doc_blob;
              if (this.doc_blob) {
                  doc.renderAsync(this.doc_blob, this.doc_preview.nativeElement)
                  .then(x => console.log("docx: finished"));
              }
         },
         error: (error: any) => {},
     })
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}

This code snippet may contain errors, but by following this approach, you should be able to resolve your issue!

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

What is the most effective method for data binding using [innerHTML] in Angular 4?

One issue I've encountered is that in Angular 4, there are limited ways to perform data binding between HTML and TypeScript, such as {{myText}}, [], (), and [innerHTML]="myText". Which method is the most effective for binding a simple variable to HTM ...

Trouble arises when default route in Angular uses '' and '**' for 404 errors as intended

Within my app-routing.module file, I have set up child routes along with an empty route for the default login page and a '**' route for handling 404 errors. Below is the code snippet: const routes: Routes = [ { path: 'dashboard' ...

Encountering issues with loading series on Highchart in Angular 4 due to duplication restrictions

I recently integrated highchart into my Angular 4 application, but I encountered a problem where the same series is being loaded twice. The issue seems to be related to the addSeries method, which is triggered by the @Input() set stressTestResults declarat ...

Why is the mat-toolbar and mat-toolbar-row in the footer (shared) component of Angular 6 not showing up correctly, despite no error being reported?

Despite reviewing similar questions, I have not been able to find a solution for my specific issue. I have searched through the following links but failed to find a solution: Angular Material v6 Mat-Footer - "Cannot read property 'template' of ...

nx serve is failing to recognize the import statement for "@angular/localize/init"

Currently, I am in the process of incorporating @angular/localize into my project within an Nx workspace ([email protected]). To achieve this, I am adhering to the official guidelines outlined at: https://angular.io/guide/i18n-overview In particular, ...

Angular: seamlessly transferring all directives from parent component to child without interference

Imagine we have a component called main that includes another one named sub. I would like for the directive passed to main in the client side, such as: <main dir1='dirval1'></main> for the main component to act as a thin wrapper and ...

The Kubernetes cluster unexpectedly closes down following a period of processing

My GCP cluster is hosting a NodeJS server. The server functions flawlessly when run locally, but mysteriously stops without any error messages when I attempt to send a post request to a specific route. This post request is supposed to trigger the sending o ...

What is the method to retrieve Response Headers in cases of an empty response?

Currently, I am working with Angular2 and dotcms. My goal is to retrieve response headers after making a call to subscribe. const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) .append('Access ...

Mastering the TypeScript syntax for executing the MongoDB find method

Having trouble properly typing the find method of MongoDB in my TypeScript project that involves MongoDB. Here's the snippet I'm working on: import { ReitsType } from '@/app/api/types/reits'; import { NextRequest, NextResponse } from &a ...

Error: Attempting to access the 'EventEmitter' property of an undefined variable in a Node.js TypeScript environment

I have a Node.js application written in TypeScript. In my code, I am utilizing the 'EventEmitter' class to trigger a change in a variable's value. Here is the relevant snippet: import events from 'events'; public async updateSt ...

Encountering a "undefined response" issue within an Angular

I am encountering an issue when trying to fetch data from a REST API. Upon logging the response, I am getting an undefined value. How can I resolve this? I have confirmed that the API is sending data by checking my network tab in the developer tool. getPro ...

Creating a custom login directive in Angular 2 and utilizing location.createComponent for dynamic

Incorporating a login system into my Angular app has been a priority for me lately. I came across a helpful resource here that outlines the process. However, I encountered an issue with the custom RouterOutlet directive as shown below: import { ElementRef ...

Angular: Comparing the Performance of Switch Statements and Dictionary Lookups

Having trouble deciding between two options for parsing URL parameters? Both seem suboptimal, but is there a better way to handle this situation? If you have any suggestions for a plausible Option #3, please share. Let's assume we are dealing with up ...

Utilizing dispatch sequentially within ngrx StateManagement

I have been working on a project that utilizes ngrx for state management. Although I am still fairly new to ngrx, I understand the basics such as using this.store.select to subscribe to any state changes. However, I have a question regarding the following ...

Which specific part is the perfect choice?

I am currently in the process of developing a small app that connects to an API through Node.js and stores all posts in MongoDB. While I have completed this part successfully, I am now faced with the challenge of creating the front-end interface. The use ...

Uncovering the origins of computed object keys in TypeScript

I am currently working on a project where I need to easily define and use new plugins using TypeScript in my IDE. My folder structure looks like this: src │ ... └── plugins └── pluginA | index.ts └── pluginB | index. ...

Refresh Information Stripe

I'm currently working on implementing Stripe, and utilizing metadata in the process. Everything works smoothly until I come across a scenario where I need to update a value in the metadata to determine if a specific uuid has been used before. pay ...

Challenges with Typescript Integration in Visual Studio 2013

Currently diving into typescript as a newbie while going through the Angular tutorial using Visual Studio 2013 for work, which is also new to me. The frustrating part is that Visual Studio seems to be assuming I am going to use a different language (judgin ...

What is the best way to bring in a variable initialized by an IIFE from a JavaScript file into a TypeScript class?

I'm currently working towards integrating the steelseries.js library (found at https://github.com/HanSolo/SteelSeries-Canvas) into a Grafana plugin built with React. It's quite a complex task, but I'm up for the challenge. Right now, my ma ...

Adding markers to a map in Angular 2 using ngOnInit after initialization

Embarking on my Angular journey by creating a sample app incorporating GoogleMaps. import { Component, Input, OnInit, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { FormControl } from '@ ...