Mastering Angular's ContentChild for Dynamic Component Interaction
Angular's ContentChild is a powerful decorator that allows a parent component to gain direct access to an element projected into its content by a child component. This is crucial for scenarios where the parent needs to manipulate or interact with dynamically inserted DOM elements or components for enhanced UI control and behavior. This challenge will test your understanding of how to leverage ContentChild effectively.
Problem Description
Your task is to implement a scenario where a parent component (ParentComponent) displays a list of items. For each item, a child component (ChildItemComponent) is used. The ParentComponent needs to dynamically update a property on each ChildItemComponent based on its own state. Specifically, you will use ContentChild to get a reference to the ChildItemComponent instances projected into the ParentComponent's template.
Requirements:
-
ChildItemComponent:- Should have a public property, for example,
isActive: boolean, which can be toggled. - Should have a simple template that displays some text and visually indicates its
isActivestate (e.g., by changing background color).
- Should have a public property, for example,
-
ParentComponent:- Should have a method, for example,
toggleActiveState(), that will be called to toggle theisActivestate of all projectedChildItemComponentinstances. - Should use
ContentChildrento query for all instances ofChildItemComponentprojected into its content. - The
toggleActiveState()method should iterate through the queriedChildItemComponentinstances and update theirisActiveproperty.
- Should have a method, for example,
-
Projecting Content:
- The
ParentComponent's template should use<ng-content>to project theChildItemComponents.
- The
Examples
Example 1: Initial State
ParentComponent Template:
<div>
<h2>Item List</h2>
<ng-content></ng-content>
<button (click)="toggleActiveState()">Toggle All Active</button>
</div>
ChildItemComponent Template:
<div [style.backgroundColor]="isActive ? 'lightblue' : 'white'">
Item Content
</div>
ParentComponent Logic:
import { Component, ContentChildren, QueryList } from '@angular/core';
import { ChildItemComponent } from './child-item.component';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
})
export class ParentComponent {
@ContentChildren(ChildItemComponent) childItems!: QueryList<ChildItemComponent>;
toggleActiveState() {
this.childItems.forEach(item => {
item.isActive = !item.isActive;
});
}
}
ChildItemComponent Logic:
import { Component } from '@angular/core';
@Component({
selector: 'app-child-item',
templateUrl: './child-item.component.html',
})
export class ChildItemComponent {
isActive: boolean = false;
}
AppModule (or relevant module):
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ParentComponent } from './parent.component';
import { ChildItemComponent } from './child-item.component';
@NgModule({
declarations: [
AppComponent,
ParentComponent,
ChildItemComponent,
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
App.component.html (to demonstrate projection):
<app-parent>
<app-child-item></app-child-item>
<app-child-item></app-child-item>
<app-child-item></app-child-item>
</app-parent>
Expected Initial Output (Visual):
Three ChildItemComponent instances are displayed, all with white backgrounds. A button "Toggle All Active" is visible.
Explanation:
The ParentComponent is configured to receive content projections. It uses @ContentChildren to find all ChildItemComponent instances within its projected content. Initially, all isActive properties are false.
Example 2: After Clicking the Button
Action: User clicks the "Toggle All Active" button in ParentComponent.
Expected Output (Visual):
All three ChildItemComponent instances now have a light blue background, indicating their isActive property has been toggled to true.
Explanation:
When the button is clicked, toggleActiveState() is called. It iterates through the QueryList of childItems and sets each item.isActive to true. The ChildItemComponent's template reacts to this change, updating the background color.
Example 3: Toggling Again
Action: User clicks the "Toggle All Active" button again.
Expected Output (Visual):
All three ChildItemComponent instances return to having a white background, indicating their isActive property has been toggled back to false.
Explanation:
The toggleActiveState() method toggles the boolean values again, successfully reverting the state.
Constraints
- The solution must be implemented using Angular's TypeScript framework.
- You must use the
@ContentChildrendecorator to query forChildItemComponentinstances. - The
ParentComponentshould not directly manipulate the DOM of theChildItemComponent; it should interact with its public properties/methods. - All components should be declared in a standard Angular module.
Notes
- Remember that
ContentChildrenqueries for elements projected into the component. This is different fromViewChildrenwhich queries elements within the component's own template. - The
QueryListprovided by@ContentChildrenis dynamic. It updates as content is added or removed. You can subscribe to itschangesobservable for more complex scenarios, though it's not required for this specific challenge. - Ensure your component selectors are unique and correctly registered.
- Think about when the
QueryListbecomes available.ngAfterContentInitis often the lifecycle hook where you can reliably access and manipulate projected content.