Implementing dependency injection in TypeScript / Angular for rapid prototyping

As a newcomer to TypeScript and Angular 7, I am working on implementing a feature where certain menu components are displayed based on the user's permissions (determined by JWT token role).

My approach involves using GuardServices and calling the canActivate method in a filter loop to dynamically generate the menu based on the user's roles.

@Component({
    selector: 'app-root',
    templateUrl: 'app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent {
    public appPages = [
        {
            title: 'Dashboard',
            url: '/app/dashboard',
            icon: 'home'
        },
        {...},
        
        {
            title: 'Employees',
            url: '/app/employees',
            icon: 'contacts',
            guard: AdminGuardService
        },
        {
            title: 'Logout',
            url: '/app/logout',
            icon: 'log-out'
        }
    ];

    public authenticated = false;
    public position = 'end';

    constructor(
        private platform: Platform,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar,
        private router: Router,
        private auth: AuthenticationService
    ) {
        this.initializeApp();
    }

    initializeApp() {
        (...)
    }

    getAppPages(): any {
        return this.appPages.filter(page => {
            if (page.guard != null) {
                page.guard.prototype.auth = this.auth;
                return page.guard.prototype.canActivate();
            }
            return true;
        });
    }
}

In this code snippet, I am manually injecting the authService required by the AdminGuardService. However, this method is not scalable as I may need other services with different dependencies in the future. Therefore, I would like to delegate dependency injection to Angular and simply call the canActivate() method.

@Injectable({
    providedIn: 'root'
})
export class AdminGuardService implements CanActivate {

    constructor(public auth: AuthenticationService) {
    }

    canActivate(): boolean {
        return this.auth.isAuthenticated() && (this.auth.roles.includes('Admin') || this.auth.roles.includes('UserAdmin'));
    }
}

Thank you!

Answer №1

To achieve your desired outcome, there are various approaches you can take.

If you wish to update these components whenever there is a change in authentication status, one option is to convert it into an observable, subscribe to it, and filter the paths that the user has permission to access. By using observables, your HTML content can asynchronously load the items and update when necessary. I personally find this site extremely helpful and enlightening. This approach is particularly useful for reacting to changes when utilizing Firebase auth with roles/teams/public/private settings.

On the other hand, if you only need this functionality during the initialization phase of the application, you could create an injectable token (provided through factory method useFactory), or utilize APP_INITIALIZERs by assigning the required providers to the factory method. For more information on how to use factory methods, you can visit this page about useFactory.

Answer №2

After some troubleshooting, I found a solution by utilizing Angular's default injector to obtain an instance of the necessary class:

import {Injector} from '@angular/core';

constructor(
    ...
    private injector: Injector
) {
    this.initializeApp();
}


getAppPages(): any {
    return this.appPages.filter(page => {
        if (page.guard != null) {
            return this.injector.get(page.guard).canActivate();
        }
        return true;
    });
}

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

Custom toString() method not executed when object is created using object literal syntax

Currently, I am facing an issue with my code. I have a class called Foo which has overridden the default implementation of toString(). class Foo { bar: string; toString(): string { return this.bar; } } Whenever I create a new variable usi ...

Having trouble with unresponsive pages when adjusting filters

My page loads dynamic charts from an API, each chart represents different equipment such as A01, A02, etc. These charts display uptime and downtime data with over 300 records. However, when I change the filter to yearly, which fetches more records from th ...

Listening to changes in a URL using JQuery

Is there a way to detect when the browser URL has been modified? I am facing the following situation: On my webpage, I have an iframe that changes its content and updates the browser's URL through JavaScript when a user interacts with it. However, no ...

Preserve final variable state - Angular

My function looks like this: flag: boolean = false; some_function(){ var foo = some_num_value; var bar = foo; // Storing value in a separate variable if(this.flag){ v ...

Error Message: "Unable to locate module for Angular 5 UI Components packaging"

In the process of developing UI Components to be used in various web projects throughout the company, we are aiming to publish these components as an npm package on our local repository. It is crucial for us to include the sources for debugging purposes. F ...

Is it possible to ensure that all values from a specific type are represented in an enum collection?

I have a Prisma enum that I've defined (not in TypeScript), and I'm curious if it's possible to synchronize a TypeScript String enum with the generated Type from the Prisma Client. Here are the key details: export const GroupInvitationSta ...

Create a one-of-a-kind Angular 6 material table component featuring unique custom columns

My goal is to streamline the process of creating custom material tables by using a specialized table component that allows me to quickly generate unique tables for different data sources with built-in pagination and sorting. All I need to provide are the d ...

Error: The function $compile does not exist

Currently, I am working on developing an AngularJS directive using TypeScript. While testing my code in the browser, I encountered the following error: TypeError: $compile is not a function at compileComponent.js:14 Interestingly, the TypeScript compiler ...

Advanced Typescript contains a parameter that specifies the type of callback function

Is it possible to create a function that is more typesafe than the current implementation? public addBusinessRule(targetProperty: string, dependentProperties: string[], callback: (dep0: any, dep1: any, ...)): void { // s ...

Deploying angular rendered application on a shared server

After developing an Angular 7 app and implementing Angular universal for SEO purposes, it has come to my attention that deploying it on a shared server is not possible due to the requirement of Node.JS to run the script file. My hosting plan restricts the ...

Tackling the white-source security problem in npm libraries

A security advisory from White-source has identified high vulnerability issues with certain libraries used in your repository, specifically with yargs-parser: 1. build-angular-0.13.8.tgz (Root Library) node-sass-4.11.0.tgz sass-graph-2.2 ...

Utilize the .mat-column-name attributes to apply custom styles to a nested component within Angular Material

Within the Child component, an Angular material table is created with columns passed as input: <table mat-table> <ng-container *ngFor="let col of columns" [matColumnDef]="col"> </table> @Input() columns: string[] T ...

Is there a way to obtain a unique response in TestCafe RequestMock?

With Testcafe, I have the capability to simulate the response of a request successfully. I am interested in setting up a caching system for all GET/Ajax requests. The current setup functions properly when the URL is already cached, but it fails to prov ...

Angular 2 wrap-up: How to seamlessly transfer filter data from Filter Component to App Component

A filtering app has been created successfully, but there is a desire to separate the filtering functionality into its own component (filtering.component.ts) and pass the selected values back to the listing component (app.ts) using @Input and @Output functi ...

Discovering the type of a generic class in TypeScript

Class B extends a generic class A, and I am trying to infer the generic type of A that B is extending. The code snippet below demonstrates this. In earlier versions of TypeScript, this worked correctly for me. However, in my current project using version ...

"Mastering the Art of Bootstrapping Providers in Angular 2

Currently using rc2, I have a TypeScript file named config that I want to access throughout my application. To achieve this, I bootstrapped it in my main.ts like this: bootstrap(MyDemoApp, [ provideForms(), disableDeprecatedForms(), APP_ROUTER_PROVIDERS ...

Could Angular be automatically applying margins or padding to my component?

I'm encountering a minor issue.. I'm attempting to create a "Matrix" to construct a snake game in angular, but there seems to be an unremovable margin/padding. Below is my code: <!-- snake.component.html --> <div id="ng-snake-main&q ...

Unable to activate keyboard within Ionic View version 3

I am facing a peculiar issue with my apk created in ionic 3. Once compiled, the main screen appears perfectly normal. https://i.sstatic.net/F3eS7.png However, when I try to type using the keyboard, and then return to the main screen, this is what happens ...

Using Angular components that have @Input properties in a module separate from where they are originally defined

There is a component with an @Input declared below: @Component({ selector: 'app-api-error-message', templateUrl: './api-error-message.component.html', styleUrls: ['./api-error-message.component.scss'], inputs: ['E ...

The system encountered an issue: unable to determine the property 'endsWith' of an undefined value

As I delve into writing my initial unit tests for the components, certain issues have arisen: TypeError: Cannot read property 'endsWith' of undefined. How can I define the data.iconPath in my test to resolve this error? Presenting my component ...