Exploring Service Injection and Factory Pattern in Angular 8

After going through various articles and official Angular guides, it appears that they were unable to assist me in solving my task. Here's what I wanted to achieve:

Imagine having an Angular application with a product listing page. Additionally, this app will include a category listing page and potentially more listing pages in the future. These pages are similar and share a common component - the data table.

<app-data-table [type]="'product'"></app-data-table>

This is how the implementation looks:

import {Component, Input, OnInit} from '@angular/core';
import {DataFactoryService} from "../data-factory.service";

@Component({
  selector: 'app-data-table',
  templateUrl: './data-table.component.html',
  styleUrls: ['./data-table.component.css']
})
export class DataTableComponent implements OnInit {

  @Input() type: string;

  private data: any[];

  constructor(private dataFactory: DataFactoryService) { }

  ngOnInit(): void {
    this.dataFactory.getServiceBy(this.type).selectAll();
  }

}

To make this component service type agnostic, I created and injected the DataFactory:

import { Injectable } from '@angular/core';
import {ProductService} from "./product.service";
import {CategoryService} from "./category.service";
import {DataService} from "./data.service";

@Injectable({
  providedIn: 'root'
})
export class DataFactoryService {

  private serviceTokenMapping = {
    "product": ProductService,
    "category": CategoryService
  };

  constructor() { }

  public getServiceBy(token: string): DataService {
    return new this.serviceTokenMapping[token];
  }
}

For products and categories, we have two services with a basic abstract class:

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

@Injectable({
  providedIn: 'root'
})
export abstract class DataService {

  abstract selectAll(): any[];
}

import { Injectable } from '@angular/core';
import {DataService} from "./data.service";
import {Product} from "./product";

@Injectable({
  providedIn: 'root'
})
export class ProductService implements DataService {

  constructor() {}

  public selectAll(): Product[] {
    console.log(`ProductService...`);
    return [];
  }
}

import { Injectable } from '@angular/core';
import {DataService} from "./data.service";
import {Category} from "./category";

@Injectable({
  providedIn: 'root'
})
export class CategoryService implements DataService {

  constructor() {}

  public selectAll(): Category[] {
    console.log(`CategoryService...`);
    return [];
  }
}

This implementation works as expected, where the type of table (product or category) corresponds to the respective page. However, I'm curious if there are better Angular-style approaches for providers, DI, etc., to achieve such a requirement.

Answer №1

It seems that you are creating instances of ProductService and CategoryService every time you call

this.dataFactory.getServiceBy(this.type)
, instead of utilizing Angular's dependency injection.

To improve this, consider using the providers property in the @NgModule decorator to define tokens for each dependency and then retrieve them using @Inject or injector.get.

export interface DataService {
  selectAll(): any[];
}

@Injectable({
  providedIn: 'root'
})
export class ProductService implements DataService {
  public static TOKEN = new InjectionToken<DataService>('ProductService_TOKEN');
  constructor() {}

  public selectAll(): Product[] {
    console.log(`ProductService...`);
    return [];
  }
}

@Injectable({
  providedIn: 'root'
})
export class CategoryService implements DataService {
  public static TOKEN = new InjectionToken<DataService>('CategoryService_TOKEN');
  constructor() {}

  public selectAll(): Category[] {
    console.log(`CategoryService...`);
    return [];
  }
}

@NgModule({
    providers: [
     {
        provide: ProductService.TOKEN,
        useExisting: forwardRef(() => ProductService),
        multi: false
     },
     {
        provide: CategoryService.TOKEN,
        useExisting: forwardRef(() => CategoryService),
        multi: false
     }
   ]
})
export class YourModule {}

@Component({
  selector: 'app-data-table',
  templateUrl: './data-table.component.html',
  styleUrls: ['./data-table.component.css']
})
export class DataTableComponent implements OnInit {

  @Input() type: string;

  private data: any[];

  constructor(
     @Inject(ProductService.TOKEN) private dataService: DataService,
     private injector: Injector
   ) { }

  ngOnInit(): void {
    this.dataService.selectAll();
    this.injector.get<DataService>(CategoryService.TOKEN).selectAll();
  }

}

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

An issue arises with launching karma.js when importing node-openid-client in a TypeScript specification file

Utilizing the node-openid-client library for OpenIDConnect based authentication with an OpenID Provider. Encountering challenges while attempting to write test cases for the program. The application runs smoothly from node CLI, obtaining the code and toke ...

Developing React component libraries with TypeScript compared to Babel compiler

Currently, I'm utilizing the babel compiler for compiling my React component libraries. The initial choice was influenced by Create React App's use of the same compiler. However, I've encountered challenges with using babel for creating libr ...

Struggling with the compilation of this Typescript code

Encountering a compile error: error TS2339: Property 'waitForElementVisible' does not exist on type 'signinPage' SigninPage code snippet: export class signinPage{ constructor(){ emailInput: { selector: 'input[type ...

Input for uncomplicated changing identifier

I am looking to create types for dynamic keys that will be of type number. I have two similar types defined as follows: type UseCalculatePayments = () => { totalPayments: number; aggregate: number; condition: boolean; }; type UseCalculateCommissio ...

Browser not reflecting changes made to Angular 2 code

Currently facing a challenging issue with my express/angular2 app. Despite using nodemon and seeing the Express code updating fine, I am experiencing issues where my components and services are not updating when I reload the page in the browser. Even aft ...

Optimizing Node.js and Express routes by separating them into individual files: a

When using Express in a Node project along with Typescript, what are the best practices for express.Router? For instance, it is recommended to follow a directory structure like this: |directory_name ---server.js |--node_modules |--routes ---in ...

Can you guide me on utilizing the drag and drop functionality in Angular 8 CDK to merge two cards together?

My current challenge involves attempting to drag one card over another card in order to effectively "merge" or "combine" them, similar to the action of dragging an image onto a folder on a desktop. Despite utilizing HTML5 native drag and drop functionalit ...

Streamline copyright verification with Angular

We are currently working on an angular application that we plan to release as open-source. We make sure to include copyright information in every file, specifically in the .ts and .scss files. However, being human, there are times when we may forget to ad ...

Leveraging the @Input decorator to send external data into the Angular component

In my Ionic app, I am utilizing an Angular component. Within this component, there is a variable called headerText that is supposed to be initialized from the page where the component is being used. The issue arises when the headerText variable is always ...

Steps for importing a CommonJS module that exports as a callable into TypeScript

I am dealing with a project that has a mixture of JavaScript and TypeScript files. Within the project, there is a JS library that follows this structure: module.exports = () => { // logic dependent on environment variables // then... return { ...

Issue with MongoDB $push within an Array of Arrays: The shorthand property 'elements' does not have a value available in scope

I am encountering an issue while trying to insert data into Elements in MongoDB using TypeScript. Any suggestions on how to resolve this problem? Attempt 1 Error: I am receiving an error message stating "No value exists in scope for the shorthand property ...

The implementation of async await within a switch case statement is failing to function properly

Is it possible to wait for the resolution of a promise within a switch case statement by using the keyword await? I am facing an issue with my Angular component where the following code is causing crashes in my application. switch (this.status) { ...

Encountering a "404 not found" error when trying to update private packages with ng update

I am in the process of updating my Angular app from version 13 to 14, which involves a private package called company-package hosted at company.com/.... Unfortunately, this package is not scoped and does not start with an @... My npm version is currently ...

Checking a sequence using a list of strings

I have an array containing a list of IDs: var listId: string[] = []; var newId: boolean; for (let i in data.chunk) { listId.push(data.chunk[i].aliases[0]); } My objective is to compare a new ID with the entire list. If the new ID is found in the list ...

Using @material-ui/core/useScrollTrigger in a Next.js application: a step-by-step guide

I have been researching the Material-UI documentation for useScrollTrigger and attempting to implement it in Next.js to replicate the Elevate App Bar. https://material-ui.com/components/app-bar/#usescrolltrigger-options-trigger import React from "react"; ...

The expandable column headers in Primeng are mysteriously missing

I'm facing an issue with my expandable row in Angular2 using Primeng2, where the column headers for the expandable columns are not displaying. Below is the code snippet of my table with expandable rows: <p-dataTable [value]="activetrucks" expanda ...

Angular 2 - AOT Compilation Issue: Running Out of JavaScript Heap Memory

I've been working on an angular2 project and when I try to build the AOT package using the command below, I encounter errors: ng build --aot --prod The errors returned are related to memory allocation failures and out of memory issues in the JavaS ...

How to handle an already initialised array in Angular?

I came across an interesting demo on exporting data to Excel using Angular 12. The demo can be found at the following link: This particular example utilizes an array within the component TypeScript file. import { Component } from '@angular/core' ...

Generating a fresh instance of input value in Angular2

In the hierarchy of components, I have a grand parent component where I am passing the "selectedValue" to the parent component like so: @Component({ selector: 'grand-parent', template: '<parent [details]="selectedValue" ></par ...

Tips for updating a single event in eventSource after it has been dragged and dropped in fullcalendar-angular

I have successfully set up the calendar using @fullcalendar/angular v4.2 and am fetching data from my backend API using eventSources. However, I am facing an issue with dragging events to a new date in the month view. <full-calendar #calendar ...