Hone logo
Hone
Problems

Mastering Content Projection with ContentChild Signals in Angular

Angular's content projection mechanism allows components to render content from their parent. The ContentChild decorator is a traditional way to access projected content. This challenge focuses on implementing the modern, signal-based approach to accessing projected content using contentChild signals. Mastering this will enhance your ability to create reusable and dynamic Angular components.

Problem Description

Your task is to create an Angular component that accepts projected content and allows a child component to access and manipulate that projected content using contentChild signals.

Key Requirements:

  1. Parent Component (ParentComponent):

    • This component should have a template that includes a <ng-content> element to project content.
    • It should also include an instance of a child component (ChildComponent) within its template.
    • The parent component should project some content (e.g., a <div> with text) into the <ng-content> slot.
  2. Child Component (ChildComponent):

    • This component needs to access an element projected by its parent.
    • Use the contentChild signal in Angular to get a reference to a specific DOM element or component projected into its <ng-content> slot.
    • The ChildComponent should display a message indicating whether the projected content has been found.
    • As a demonstration, when the projected content is available, the ChildComponent should be able to read a specific attribute from the projected element.

Expected Behavior:

  • When the ParentComponent renders, it should project its content.
  • The ChildComponent, when initialized, should successfully find the projected content using contentChild signals.
  • The ChildComponent should display a confirmation message and the value of a specified attribute from the projected element.

Edge Cases:

  • What happens if no content is projected? The ChildComponent should gracefully handle the absence of projected content.
  • Consider projecting different types of content (e.g., text nodes, other components) and how contentChild signal would behave. For this challenge, focus on a simple DOM element.

Examples

Example 1: Basic Projection and Access

<!-- parent.component.html -->
<app-child>
  <div data-test-id="projected-data">Hello from projected content!</div>
</app-child>
// parent.component.ts
import { Component } from '@angular/core';
import { ChildComponent } from '../child/child.component';

@Component({
  selector: 'app-parent',
  standalone: true,
  imports: [ChildComponent],
  templateUrl: './parent.component.html',
})
export class ParentComponent {}
// child.component.ts
import { Component, inject, contentChild, signal } from '@angular/core';
import { NgIf } from '@angular/common';

@Component({
  selector: 'app-child',
  standalone: true,
  imports: [NgIf],
  template: `
    <p>Projected content status: {{ contentFound() ? 'Found' : 'Not Found' }}</p>
    <p *ngIf="contentFound()">Projected data attribute: {{ projectedDataAttribute() }}</p>
  `,
})
export class ChildComponent {
  // Use contentChild signal to access a div with the selector 'div[data-test-id]'
  // The signal should return the element or null if not found.
  // Initialize as null.
  projectedElement = contentChild('div[data-test-id]');

  contentFound = signal(false);
  projectedDataAttribute = signal<string | null>(null);

  ngAfterContentInit() {
    if (this.projectedElement()) {
      this.contentFound.set(true);
      this.projectedDataAttribute.set(this.projectedElement()?.getAttribute('data-test-id') ?? null);
    }
  }
}

Expected Output (rendered in the browser for Example 1):

Projected content status: Found
Projected data attribute: projected-data

Explanation:

The ParentComponent projects a <div> element with data-test-id="projected-data". The ChildComponent uses contentChild('div[data-test-id]') to get a reference to this <div>. In ngAfterContentInit, it checks if projectedElement() has a value, sets contentFound accordingly, and then retrieves and sets the data-test-id attribute.

Example 2: No Projected Content

<!-- parent.component.html (modified to not project content) -->
<app-child></app-child>
// parent.component.ts (same as Example 1)
// ...
// child.component.ts (same as Example 1)
// ...

Expected Output (rendered in the browser for Example 2):

Projected content status: Not Found

Explanation:

Since no content is projected within the app-child tags in the ParentComponent's template, contentChild signal will return null. The ChildComponent will correctly report "Not Found" and will not attempt to access the data-test-id attribute.

Constraints

  • You must use Angular version 16 or later, which supports signals.
  • The ChildComponent must utilize the contentChild signal to access the projected content.
  • The solution should be implemented using TypeScript.
  • Avoid using ViewChild or ContentChild decorators from older Angular versions.
  • Focus on projecting a simple DOM element for the primary challenge.

Notes

  • The contentChild signal returns a Signal<T | undefined>, where T is the type of the element or component being queried.
  • You'll need to implement the ngAfterContentInit lifecycle hook to access the projected content reliably, as it's populated after the content has been projected.
  • Consider how you would select different types of elements using CSS selectors within the contentChild signal's argument.
  • Think about how you might handle multiple projected elements if that were a requirement (though not for this challenge).
Loading editor...
typescript