Angular 8: Implementing unique service instances for each instance of a shared component

In my current project, I am working on developing reusable d3-based dashboard components within Angular 8. The goal is to create components such as a barchart that can be easily packaged into a module and reused without requiring any modifications to the component code, not even the constructor parameters. Additionally, I aim to have the ability for multiple sibling instances of these components to display different sets of data.

To achieve this, I have structured the component's API with input parameters for simple display configuration and a service interface that all components must adhere to for handling data and interactions. This service interface is implemented as an abstract base class, which the component's constructor accepts as a parameter (preventing users/developers from modifying constructor parameters or altering any component code).

However, one challenge I am facing is how to provide different implementations of the service base class to individual component instances without having to modify the constructor parameters. I previously attempted creating an abstract base class for the bar chart and then derived instances for specific bar chart services; however, this approach did not inherit the template and styles from the component base class.

export class BarChartComponent implements AfterViewInit
{
    ...

    public constructor(public service: BarChartService) {}

    public ngAfterViewInit() {
        ...

        this.service.dataInit$.subscribe(() => {
            let data = this.service.barChartData;
            this.drawChart(data);
        });

        this.service.dataRefresh$.subscribe(() => {
            let data = this.service.barChartData;
            this.drawChart(data);
        });
    }

    private drawChart(data): void {
        ...
    }
}

@Injectable()
export abstract class BarChartService {

    abstract barChartData: any;

    abstract dataInit$: Observable<boolean>;
    abstract dataRefresh$: Observable<boolean>;

    abstract barChartSelect(string): void;
    abstract barChartXValue: (d: any) => any;
    abstract barChartYValue: (d: any) => any;
}

I am striving for multi-instance components that are reusable and capable of displaying various datasets. Any insights or suggestions on this matter would be highly appreciated.

To provide some clarity, here is an example of what a derived class of barChartService, like ApprovalsBarChartService, entails (where ApprovalService handles backend access logic and crossfilter shared across the dashboard):

@Injectable()
export class ApprovalsBarChartService implements BarChartService {

    private init = new Subject<boolean>();
    dataInit$ = this.init.asObservable();

    private refresh = new Subject<boolean>();
    dataRefresh$ = this.refresh.asObservable();

    public get barChartData(): any {
        return this.service.approvalsGroupedByApprover.all();
    }

    public barChartXValue = function (d) { return d.key; };
    public barChartYValue = function (d) { return d.value; };

    public constructor(public service: ApprovalService) {   

        this.service.init$.subscribe(() => {
            this.init.next(true);
        });

        this.service.refresh$.subscribe(() => {
          this.refresh.next(true);
        });
    } 

    public barChartSelect(processor: string): void {
        this.service.approvalsIndexedByApprover.filter(processor);
        this.service.selectedProcessor = processor;
        this.service.selectItems = +this.service.xfilter.groupAll().reduceCount().value();
        this.service.refreshCharts();
    }
}

Answer №1

Is it necessary for `BarChartService` to be classified as a service? If you are not planning on reusing the same instance of the service across different components, then having it as a service may not be required.

Instead of injecting the service, you can simply create an instance of it inside the constructor of your component. So rather than:

public constructor(public service: BarChartService) {}

You can do this:

public service: BarChartService;
public constructor() {
    this.service = new BarChartService();
}

Keep in mind that since the class is abstract, you will need to instantiate the concrete subclasses.

If you do require multiple instances of the service injected into different components, you can provide distinct injection tokens for each instance. This way, you can choose which instance to inject into your components by specifying the corresponding injection token in their constructors:

Create two injection tokens:

export const MyToken1 = new InjectionToken<BarChartService>('MyToken1');
export const MyToken2 = new InjectionToken<BarChartService>('MyToken2');

Assign a unique service instance to each token:

@NgModule(...) 
export class ... {
    ...
    providers: [
        {provide: MyToken1, useValue: new MyServiceInstance(1, 2, 3)},
        {provide: MyToken2, useValue: new MyServiceInstance(4, 5, 6)},
    ]
}

Then, in your components, specify which service to inject:

public constructor(@Inject(MyToken1) public service: BarChartService) {}

Answer №2

Originally, I attempted to achieve a certain goal but realized it was not feasible.

Fortunately, I found an alternative solution:

Solution: Instead of using the BarChartService, all functionalities can be incorporated as @Input parameters except for BarChartSelect, which should trigger an @Output event caught in the parent component. This event should then call a method in the ApprovalService, a data retrieval service housing the crossfilter for the entire dashboard. I recently discovered that passing functions as input parameters to customize behavior within components is possible and highly effective.

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

Tips for coding in Material-UI version 5: Utilizing the color prop in the Chip component by specifying

Is there a better way to type the MUI Chip prop color when the actual value comes from an object? Using any doesn't seem like a good option. Additionally, is keyof typeof CHIP_COLORS the correct approach for typing? import { Chip, Stack } from "@ ...

Issue with the loading of Firebase in Chrome extension's background script is inconsistent

I am currently working on developing a Chrome extension that utilizes Google Firebase for authentication. In my project, I am employing webpack for building purposes, with Firebase being utilized in the background script. However, during the initial initi ...

Deleting an element from a two-field object in TypeScript 2

I am working with a 2-field object structure that looks like this { id: number, name: string } My goal is to remove the name field from this object. How can I achieve this in TypeScript? I have attempted using methods like filter and delete, but all I r ...

What role does the @Input statement in the HeroDetailComponent class serve in the Angular 2 Quickstart tutorial?

Looking at the multiple components part of the Angular 2 Quickstart tutorial, we see how a component is separated from the AppComponent to enhance reusability and testing convenience. You can try out the example live demo here. In this scenario, users ar ...

Encountering CORS Issue with Golang and Gin following a Redirect Situation

I am currently working on implementing Google OAuth2 in my Go web server using Gin. I have integrated two new endpoints - /google/sign-in and /google/callback. The former receives the request and redirects to the Google auth URL, while the latter is trigge ...

Restricting the number of mat-chips in Angular and preventing the input from being disabled

Here is my recreation of a small portion of my project on StackBlitz. I am encountering 4 issues in this snippet: I aim to restrict the user to only one mat-chip. I attempted using [disabled]="selectedOption >=1", but I do not want to disable ...

Angular background image not displayed

After searching extensively, I came across many examples that didn't work for me. I'm not sure what I'm doing wrong and I need assistance! I noticed that I can see the image if I use the <img> tag, but not when I try to set it as a ba ...

Is there a way to verify whether a key within an Interface corresponds to a class that is a subclass of a specific Parent

Is there a method in typescript to ensure that a property in an interface must be of type "child subclass C, which extends class P"? example.ts import { P } from '/path/to/types' class C extends P { ... } types.ts // `C` cannot be accessed ...

Can you confirm if npm is successfully connecting through a proxy from Visual Studio 2015?

My first attempt at building an Angular site is hitting a roadblock – I can't seem to restore the json packages using NPM. It looks like there's a proxy issue since I'm behind a corporate firewall. Now, I'm trying to troubleshoot and ...

Exploring the ambiguity of explicit types with type generics

I am facing a type error issue with a Higher-Order Component (HOC): Error: Type 'number[]' is not assignable to type 'string'. Here is how I set up my HOC: const ComponentPage = renderPage(Component); However, I encounter the error ...

Implementing Node.js microservices with AWS Cognito leveraging Amplify's best practices

I am currently working on developing a Node.js API that is broken down into several small APIs (microservices) communicating with each other through requests and responses. Additionally, I am utilizing Angular for the frontend. My next step is to enhance ...

Encountering a Compilation Issue in Angular 4

After executing npm install bootstrap@next in my new Angular project, I encountered a compilation error. As a beginner with Angular, I'm seeking assistance on this issue. Compilation Error: ./node_modules/ansi-html/index.js Module build failed: ...

Attempting to deploy my Angular project on Heroku but encountering an error

While attempting to deploy my Angular project to Heroku, I followed all the necessary steps but encountered an error stating that the application failed to serve the page. As the application owner, it is advised to check the logs for more details. This can ...

Universal in Angular is malfunctioning

As a newcomer to Angular 4, I am looking to create an Angular app that is SEO friendly and supports Angular Universal (with the --universal flag after ung new or ung init). I have successfully created an Angular Universal app. However, when running the p ...

Styling Angular 5 components without scoped styles

Currently, I am facing a dilemma with my Angular component that embeds a Microsoft PowerBI Report. The powerbi-client utilizes the nativeElement from an ElementRef to inject an iframe containing the report. My goal is to customize the styling of the border ...

Tips for enhancing a TypeScript interface for a React component in (Material-UI) by utilizing styled-components

I've been struggling to find a solution for this overload issue with no luck so far. My stack includes Typescript, Styled-components, and Material-UI. I am utilizing styled(MUIButton) to extend the default Button from MUI. While my props are being pas ...

When you hover over the button, it seamlessly transitions to a

Previously, my button component was styled like this and it functioned properly: <Button component={Link} to={link} style={{ background: '#6c74cc', borderRadius: 3, border: 0, color: 'white', height: 48, padding: '0 ...

Refreshing/reloading Angular 9 SSR pages

Encountering an issue with Angular and SSR. Running angular 9, everything runs smoothly except when I refresh the page (F5), it first displays the login page before loading the current page. Previously built with angular 8, where this problem did not occur ...

Having trouble with installing angular-cli on a Windows 10 system connected to a proxy network

Attempting to setup angular-cli on Windows 10 while connected to a proxy network. Unfortunately, encountering errors when trying to install angular-cli on my machine. Specifically, receiving the following two errors in the command prompt ( 403 Forbidden - ...

Attempting to publish and install a unique angular2 component using NPM and Angular-CLI results in successful compilation only on the initial try

I am facing a peculiar and frustrating issue. The problem revolves around an Ng2 component I have developed called via-date-picker. My goal is to publish it on NPM so that it can be easily utilized in other projects. To achieve this, I converted it into a ...