Hone logo
Hone
Problems

Replicating Angular's @for Syntax

Angular's @for syntax provides a powerful and concise way to iterate over collections and render elements in your templates. This challenge asks you to implement a simplified version of this functionality as a custom directive in Angular using TypeScript. This exercise will deepen your understanding of structural directives, content projection, and template manipulation within Angular.

Problem Description

Your task is to create a custom structural directive named CustomForDirective that mimics the basic behavior of Angular's @for syntax. This directive should accept an iterable (like an array) and render its content for each item in the iterable. You'll need to handle rendering the template for each item and provide access to the current item and its index within the template's context.

Key Requirements:

  1. Directive Definition: Create a structural directive that can be applied to an element in an Angular template (e.g., <div *customFor="let item of items">).
  2. Iterable Input: The directive should accept an input property, conventionally named customForOf (or similar, corresponding to let item of items), which will be the iterable (e.g., an array).
  3. Template Rendering: For each item in the iterable, the directive must render the template it's attached to.
  4. Context Provision: Within each rendered instance of the template, the directive must provide the current item and its index in the iterable.
  5. Dynamic Updates: The directive should ideally handle basic updates to the iterable (e.g., adding or removing items) and re-render the template accordingly. For this challenge, focus on initial rendering and a basic update mechanism.

Expected Behavior:

Given an array items and a template structure, the directive should project the template multiple times, once for each element in items. Each projected template instance should have access to the current element (bound to let item) and its numerical index (bound to let index).

Edge Cases:

  • An empty iterable should result in no template being rendered.
  • Consider what happens if the input is not an iterable.

Examples

Example 1: Basic Iteration

Component Template:

<div *customFor="let user of users">
  {{ index + 1 }}. {{ user.name }}
</div>

Component Class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
})
export class UserListComponent {
  users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ];
}

Expected Output (rendered DOM):

<div>
  1. Alice
</div>
<div>
  2. Bob
</div>

Explanation: The *customFor directive iterates over the users array. For each user object, it renders the div content. The index variable is automatically provided, starting from 0.

Example 2: Empty Iterable

Component Template:

<p *customFor="let item of emptyList">
  This should not be rendered.
</p>

Component Class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-empty-list',
  templateUrl: './empty-list.component.html',
})
export class EmptyListComponent {
  emptyList: any[] = [];
}

Expected Output (rendered DOM):

(No <p> tags are rendered)

Explanation: When the input iterable is empty, the directive renders nothing.

Example 3: Handling Updates (Simplified)

Component Template:

<li *customFor="let task of tasks">
  {{ index }} - {{ task }}
</li>

Component Class:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-task-list',
  templateUrl: './task-list.component.html',
})
export class TaskListComponent implements OnInit {
  tasks = ['Buy milk', 'Walk the dog'];

  ngOnInit() {
    setTimeout(() => {
      this.tasks = [...this.tasks, 'Read a book']; // Adding an item
    }, 2000);
  }
}

Expected Output (rendered DOM after 2 seconds):

<li>
  0 - Buy milk
</li>
<li>
  1 - Walk the dog
</li>
<li>
  2 - Read a book
</li>

Explanation: Initially, the directive renders two <li> elements. After 2 seconds, the tasks array is updated. The directive should detect this change and render an additional <li> for the new task.

Constraints

  • The directive must be implemented using Angular's StructuralDirective capabilities.
  • The directive should use TemplateRef and ViewContainerRef for rendering.
  • The customForOf input property should accept any type that can be iterated over (e.g., Array).
  • The context provided to the template should include item and index.

Notes

  • You will need to create a new Angular module and component to test your directive.
  • Consider how you will extract the item and index from the iterable.
  • For handling updates, you'll need a mechanism to detect changes to the input iterable. ngDoCheck and IterableDiffer can be helpful here, though for a simpler implementation, you might focus on initial rendering and then manually re-triggering updates if needed for demonstration.
  • Think about how to bind variables like let item and let index in the template to the context you provide. This is a crucial part of replicating the @for syntax.
Loading editor...
typescript