import {
createNgModule,
inject,
Injectable,
Injector,
isStandalone,
NgModuleRef,
Type
} from '@angular/core';
import { DfBaseConfig, NATIVE_DYNAMIC_FORM_COMPONENTS } from '../../base/base.model';
import { CUSTOM_COMPONENT_TYPE, CUSTOM_DYNAMIC_FORM_COMPONENT } from '../../base/custom.component';
interface DfComponentModule {
component: Type<unknown>;
moduleRef?: NgModuleRef<unknown>;
}
@Injectable()
export class DfComponentLoaderService {
private injector = inject(Injector);
private nativeComponents = inject(NATIVE_DYNAMIC_FORM_COMPONENTS);
private customComponents =
inject(CUSTOM_DYNAMIC_FORM_COMPONENT, {
optional: true
}) ?? [];
/**
* Gets a component and module reference
* @param config The config of the component, including the `type` (and `name` for custom components)
* @return Promise<DfComponentModule> A promise with the component and module reference
*/
async getComponent(config: {
type: DfBaseConfig['type'];
name?: string;
}): Promise<DfComponentModule> {
if (config.type === CUSTOM_COMPONENT_TYPE) {
if (!config.name) {
throw new Error('Custom component name is required');
}
return this.retrieveCustomComponentModule(config.name);
}
return this.retrieveNativeComponentModule(config.type);
}
/**
* Gets component type and compiles module reference if component is not standalone
* @param component The dynamic form component to load (supports standalone components)
* @param module The module. Optional if component is a standalone component
* @return DfComponentModule A reference to the module and the component to compile
*/
private getComponentModule(component: Type<unknown>, module?: Type<unknown>): DfComponentModule {
if (!module) {
if (!isStandalone(component)) {
throw new Error(
`Component "${component?.name}" is not an Angular Standalone component. Please provide its module.`
);
}
return { component };
}
return {
moduleRef: createNgModule(module, this.injector),
component: component
};
}
private async retrieveNativeComponentModule(type: string): Promise<DfComponentModule> {
const config = this.nativeComponents[type];
if (!config) {
return Promise.reject(`The component ${type} is not registered`);
}
const result = await config.load();
return this.getComponentModule(
result[config.componentName as keyof typeof result],
config.moduleName ? result[config.moduleName as keyof typeof result] : undefined
);
}
private async retrieveCustomComponentModule(name: string): Promise<DfComponentModule> {
const customComponent = this.customComponents?.find((c) => c.componentName === name);
if (!customComponent) {
throw new Error(`The custom component "${name}" is not registered`);
}
const result = await customComponent.load();
return this.getComponentModule(
result[customComponent.componentName],
customComponent.moduleName ? result[customComponent.moduleName] : undefined
);
}
}