Dynamic Component Loading in Angular
Angular's powerful component-based architecture allows for building flexible and interactive user interfaces. A common requirement is to dynamically load and render components based on runtime conditions, such as user input or data fetched from an API. This challenge will test your ability to implement dynamic component loading in Angular.
Problem Description
Your task is to create an Angular application that can dynamically load and render different components within a designated container. You will be provided with a list of available components and a mechanism to trigger the loading of a specific component. The application should be able to:
- Define a set of available components: These components will be registered and accessible for dynamic loading.
- Create a container for dynamic components: A specific directive or component will act as the host where the dynamically loaded components will be rendered.
- Implement a mechanism to trigger component loading: This could be a button click, a dropdown selection, or any other user interaction.
- Dynamically instantiate and render the selected component: Based on the trigger, the application should create an instance of the specified component and attach it to the designated container.
- Handle component inputs and outputs: If the dynamically loaded components require input data or emit output events, your solution should correctly manage these.
- Clean up/destroy loaded components: When a new component is loaded or the container is no longer needed, the previously loaded component should be properly destroyed.
Examples
Example 1: Basic Component Loading
Scenario: Load a WelcomeMessageComponent when a button is clicked.
Input:
- A button with the text "Load Welcome Message".
- A
DynamicHostDirectiveattached to an emptyng-container. - A
DynamicComponentLoaderServicethat knows aboutWelcomeMessageComponent.
Expected Behavior:
Clicking the "Load Welcome Message" button should cause WelcomeMessageComponent to be rendered inside the ng-container.
Example 2: Loading Components with Inputs
Scenario: Load a UserProfileComponent with user data passed as input.
Input:
- A dropdown allowing selection of a user (e.g., "Alice", "Bob").
- A
DynamicHostDirectiveattached to an emptydiv. - A
DynamicComponentLoaderServicethat knows aboutUserProfileComponent. - User data:
{ id: 1, name: 'Alice', email: 'alice@example.com' }and{ id: 2, name: 'Bob', email: 'bob@example.com' }.
Expected Behavior:
Selecting "Alice" from the dropdown should render UserProfileComponent with Alice's data. Selecting "Bob" should destroy the previous instance and render UserProfileComponent with Bob's data.
Example 3: Dynamic Component with Output Event
Scenario: Load an InteractiveButtonComponent that emits an event when clicked.
Input:
- A button labeled "Show Interactive Button".
- A
DynamicHostDirective. - An
InteractiveButtonComponentthat has an@Output() clicked = new EventEmitter<string>();. - The application should display a message indicating which button was clicked.
Expected Behavior:
Clicking "Show Interactive Button" renders InteractiveButtonComponent. Clicking the rendered button inside InteractiveButtonComponent should emit an event, and the parent component should update to show a message like "Interactive button clicked with label: Click Me!".
Constraints
- The solution must be implemented using Angular version 14 or later.
- All dynamic component loading logic should be encapsulated within a service for reusability.
- Components should be loaded lazily if possible, although eager loading for demonstration is acceptable.
- The application should handle cases where the requested component is not found.
- The solution should be well-tested with unit tests covering the dynamic loading service and directive.
Notes
Consider using ViewContainerRef and ComponentFactoryResolver (or the newer approach with createComponent in Angular 13+) for dynamic component instantiation. Think about how to map component identifiers (e.g., strings) to their actual component classes. Pay attention to the lifecycle hooks of dynamically loaded components.