I am working on Angular project (Angular 14+) and following atomic design principles (creating minute level components like button, input, toggle etc.).
Most of the views (list, forms, headers) are based on JSON which is coming from server (telling what and where of components). Server also send function enums
to execute a particular functionality.
for e.g. on a button click navigate to a certain screen, or open a particular modal and take user's action and hit an API, etc. These functions (JSON based function enums) are distributed across the application (can present at any level of nesting).
So for simplicity I opted for two approaches:
- TypeScript class based functions (component models)
- Pure functions (simple import/export)
Now when writing these functions, need arises to access some of the services and third party components like Modal reference, snackbar reference, router reference or HTTP service.
For this to avoid Angular DI or other framework code, I took global reference of angular injector (injector()
function) and created a global singleton TS class with set of properties for HTTP, modal, snackbar, router ( i.e. HTTPRef
, modalRef
, snackbarRef
, routingRef
) and assigned them required services or component references.
Now I am able to use them in simple classes or inside pure functions like e.g.
// pure function example:
const openModal<T>(componentRef: ComponentType<T>, componentData?: any, dialogConfig?: MatDialogConfig, closeCB?: FunctionCall){
const dialogRef = globalConfig.matDialogRef; // singelton class ref
const openDialogRef = dialogRef.open(component,{
data: componentData
});
return { dialogRef, openDialogRef };
}
Similarly other functions global dependencies, it helped me to call in TS normal classes (component Models).
Creation of these global Ref is as follows:
AppModule ---> InitializerModule --> calling TS class methods
e.g.
<!-- InitializerModule -->
// all imports
export let NGInjectorInstance: Injector; ----- [A]
// some more code
export class InitializerModule {
constructor(private injector: Injector) {
NGInjectorInstance = this.injector; // injector reference
CLIENTS_INIT(); // calling classes init functions
GLOBAL_INIT(); // same here, will use global injector inside these classes to assign references
}
}
Classes:
GLOBAL_INIT() ---> /src/models/config/index.ts
// index.ts
import { default as GlobalConfig } from "./global.config"; // singelton class
export const globalConfig = GlobalConfig;
export default () => {
globalConfig.initSnackBarDeps();
globalConfig.initMatDialogDeps();
globalConfig.initRouterDeps();
globalConfig.initHTTPDeps();
globalConfig.initStoreDeps();
}
public initMatDialogDeps(){
this.matDialogRef = NGInjectorInstance.get<MatDialog>(MatDialog);
}
I read here that injecting service to a standalone class is an anti-pattern and here that injecting service in a class is considered anti-pattern. So even if it's how should I validate it how much I can deviate/bend to follow this approach (I also agree how much is very subjective and require more context). Is it something to be discarded as a whole or there is still room to follow this?