Hone logo
Hone
Problems

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:

  1. 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 isActive state (e.g., by changing background color).
  2. ParentComponent:

    • Should have a method, for example, toggleActiveState(), that will be called to toggle the isActive state of all projected ChildItemComponent instances.
    • Should use ContentChildren to query for all instances of ChildItemComponent projected into its content.
    • The toggleActiveState() method should iterate through the queried ChildItemComponent instances and update their isActive property.
  3. Projecting Content:

    • The ParentComponent's template should use <ng-content> to project the ChildItemComponents.

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 @ContentChildren decorator to query for ChildItemComponent instances.
  • The ParentComponent should not directly manipulate the DOM of the ChildItemComponent; it should interact with its public properties/methods.
  • All components should be declared in a standard Angular module.

Notes

  • Remember that ContentChildren queries for elements projected into the component. This is different from ViewChildren which queries elements within the component's own template.
  • The QueryList provided by @ContentChildren is dynamic. It updates as content is added or removed. You can subscribe to its changes observable 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 QueryList becomes available. ngAfterContentInit is often the lifecycle hook where you can reliably access and manipulate projected content.
Loading editor...
typescript