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:
- 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">). - Iterable Input: The directive should accept an input property, conventionally named
customForOf(or similar, corresponding tolet item of items), which will be the iterable (e.g., an array). - Template Rendering: For each item in the iterable, the directive must render the template it's attached to.
- Context Provision: Within each rendered instance of the template, the directive must provide the current
itemand itsindexin the iterable. - 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
StructuralDirectivecapabilities. - The directive should use
TemplateRefandViewContainerReffor rendering. - The
customForOfinput property should accept any type that can be iterated over (e.g., Array). - The context provided to the template should include
itemandindex.
Notes
- You will need to create a new Angular module and component to test your directive.
- Consider how you will extract the
itemandindexfrom the iterable. - For handling updates, you'll need a mechanism to detect changes to the input iterable.
ngDoCheckandIterableDiffercan 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 itemandlet indexin the template to the context you provide. This is a crucial part of replicating the@forsyntax.