Building a class following the completion of an asynchronous operation

Seeking a solution in the given code, I must wait for server calls to initialize cached objects as properties on the class. After researching, I found inquiries on waiting for multiple subscriptions, and decided to utilize forkJoin as demonstrated. However, this leads to a new dilemma - now I must wait on forkJoin. How can I achieve this without rewriting the project, which is not asynchronous?

// Question 2

In theory, I could privatize the constructor and implement a static factory method. However, I foresee encountering the same issue of awaiting the async factory method without a mechanism in place. If I opt for this approach, how can I instruct the Angular DI mechanism to a) await the factory method and b) inject the instance (providedIn 'root' exclusively denotes creating a singleton, not instantiating the instance).

export class PermissionService {

      public initialization : Promise<void>;  

      private _isAdmin : boolean;
      private appPermissionsDict : object;
      private dataPermissionsDict : object;

      baseUrl = environment.baseUrl + 'permission';

      constructor(private http: HttpClient) {
        this.initialization = this.init();
      }


    async init()
    {
      await forkJoin([
        this.getIsAdmin(),
        this.getAppPermissions(),
        this.getDataPermissions()
        ]).subscribe(([isAdmin, ap, dp]) => {
          this._isAdmin = isAdmin;
          this.appPermissionsDict = Object.assign({}, ...ap.map(x => x.id))
          this.dataPermissionsDict = Object.assign({}, ...dp.map(x => x.id))
          });
          
      //---
      // Need to wait here for properties to be set on the class
      //---
    }
}

Working with rxjs 6.5.4 / angular 9.1

Answer №1

You have the option to assign each async call to properties on your service.

// sharing the last emission with late subscribers using shareReplay(1)
readonly isAdmin$ = this.getIsAdmin().pipe(shareReplay(1));
readonly appPermissionsDict$ = this.getAppPermissions().pipe(shareReplay(1)); 
readonly dataPermissionsDict$ = this.getDataPermissions().pipe(shareReplay(1));

These properties can then be accessed wherever the service is injected. In Angular templates, the usage would be similar to this:

<ng-container *ngIf="permissionService.isAdmin$ | async">

</ng-container>

This template syntax handles async subscriptions and unsubscriptions on component destroy, eliminating the need for manual .subscribe calls, resulting in cleaner code.

You may not necessarily need to use forkJoin in this scenario. However, if you do and require values from isAdmin$, appPermissionsDict$, and dataPermissionsDict$ before proceeding, you can structure it like this:

overallPermissions$ = forkJoin({
    isAdmin: this.isAdmin(),
    appPermissionsDict: this.getAppPermissions(),
    dataPermissionsDict: this.getDataPermissions(),
})
// returns observable of { isAdmin: boolean, appPermissionsDict: object, dataPermissionsDict: object }

Subsequently, you can use this in your templates:

<ng-container *ngIf="permissionService.overallPermissions$ | async; let overallPermissions">
    <ng-container *ngIf="overallPermissions.isAdmin"></ng-container>
</ng-container>

In summary, forkJoin will only emit when all inner observables emit. Errors in any inner observable will fail silently unless caught, and long-running observables may delay access to other values. Given the limited context, separate subscriptions might be preferable over forkJoin in this instance.

Using providedIn: "root" for the permissions service seems appropriate, as a single instance is likely desired for such 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

Creating dynamic DOM elements in Angular templates by utilizing variable values as element types

There's a component I'm working with that I want to render either as a div or span dynamically. To achieve this, I've created an input variable called elementType. Now, the challenge is how to properly render it in the template. Here's ...

Strategies for reducing the number of ngIf statements in Angular's template

I'm seeking advice on how to avoid using multiple *ngIf in templates. For instance, in a component's template, depending on the route, I need to display various elements like so: <div *ngIf="route == 'page1'">Title for page 1< ...

What is the best way to clear the parent component's content from the child component in Angular?

Having an issue with Angular routes. The URLs are functioning properly, but when I navigate to the child component, specifically CreateEventComponent, the parent component's content from EventsComponent is also displayed. How can I make sure that th ...

Tips for Resolving TypeScript Error 7053 when using the handleChange function in a React Form

Seeking assistance with creating a versatile handleChange function for a React form. The goal is for the handleChange function to update the state value whenever a form field is modified, while also accommodating nested values. Below is my attempt: const ...

Get the HTML file converted to a DOCX format that is compatible with Mac Pages

I am currently working on a Next.js application using TypeScript, and I want to give users the ability to download a page as a DOCX file. Initially, I was excited to discover that this could be easily accomplished by following this method. However, after ...

Issue encountered when attempting to assign `fontWeight` within `makeStyles` using `theme` in Typescript: Error message states that the property is not

Currently, within my NextJS project and utilizing MUI, I am attempting to define a fontWeight property using the theme settings in the makeStyles function. Oddly enough, this issue only arises when building inside a docker container, as building locally po ...

Proper method for inserting a value into a string array in a React application using TypeScript

How can I properly add elements to a string array in react? I encountered an error message: Type '(string | string[])[]' is not assignable to type 'string[]' You can view the code on this playground link : Here Could it be that I&apos ...

Issue with Formgroup in Angular Reactive Form - Validation not functioning as expected

I am working with a form group which is defined below: get createItem(): FormGroup { return this.formBuilder.group({ name: ['', Validators.required], email: ['', Validators.required], mobile: ['', V ...

TypeScript issue encountered with parseInt() function when used with a numeric value

The functionality of the JavaScript function parseInt allows for the coercion of a specified parameter into an integer, regardless of whether that parameter is originally a string, float number, or another type. While in JavaScript, performing parseInt(1. ...

Issues with Angular routing in Fuse administrator and user interfaces

I am encountering an issue with navigating routes for admin and user roles, where the user role has limitations compared to the admin role. <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.1/angular.min.js"></script> const ...

What are the advantages of using any type in TypeScript?

We have a straightforward approach in TypeScript to perform a task: function identity(arg) { return arg; } This function takes a parameter and simply returns it, able to handle any type (integer, string, boolean, and more). Another way to declare thi ...

Error encountered when an Angular expression is changed after it has already been checked in a dynamically generated component

I am encountering a problem with Angular and the CdkPortal/CdkPortalHost from @angular/cdk. I developed a service that allows me to associate a CdkPortalHost with a specified name and set its Component at any given time. This is how the service is struc ...

In Angular 2, leverage a variable within the @Component styles

Here is a plunker link for you to check out: https://plnkr.co/edit/fzNJ7FLYxWbLPoxtYpEw?p=preview This is a basic angular application. I am using @ViewChild and nativeElement.offsetHeight to capture the height of a div. Can this number be used in the st ...

What prevents TypeScript from automatically inferring tuple return types in RxJs streams?

When composing an observable stream, the map function infer is a union instead of a tuple. For instance: import { Component } from '@angular/core'; import { from } from 'rxjs'; import { map, tap } from 'rxjs/operators'; expo ...

The create document feature seems to be malfunctioning for some reason. Any ideas why it's not working properly in Firebase with Angular

I've been attempting to submit user data to the Firebase Firestore database, but I'm experiencing issues with the function that is supposed to create a new collection. Despite trying different methods, none of them seem to be working for me. I ha ...

Tips for capturing an error generated by a child component's setter?

I've created an App component that contains a value passed to a Child component using the @Input decorator. app.component.html <app-child [myVariable]="myVariable"></app-child> app.component.ts @Component(...) export class AppC ...

Creating a Typescript interface where one property is dependent on another property

Let's look at an illustration: type Colors = { light: 'EC3333' | 'E91515' dark: '#100F0F' | '140F0F' } interface Palette { colorType: keyof Colors color: Colors[keyof Colors] } Is it possible for the ...

Conceal a designated column within a material angular data table based on the condition of a variable

In the morning, I have a question about working with data tables and API consumption. I need to hide a specific column in the table based on a variable value obtained during authentication. Can you suggest a method to achieve this? Here is a snippet of my ...

Angular 5 offers the ability to incorporate dynamic checkbox input into your application

Here is my code snippet: <input [type]="'checkbox'" [(ngModel)]="inputValue"> <p>Value: {{ inputValue }}</p> I'm puzzled as to why the value in inputValue remains unchanged. Can anyone shed light on this? I am unable to ...

Leveraging Angular Observables for seamless data sharing across components

As I embark on developing my very first Angular app, I have encountered a challenge with filtering a list of book objects based on their gender attribute. The issue lies in sharing data between components – specifically the filteredData variable and the ...