Implementing a Custom ngFor Directive in Angular
This challenge asks you to build a simplified version of Angular's ngFor directive. Understanding how ngFor works under the hood is a valuable exercise in Angular internals and demonstrates how directives manipulate the DOM. Successfully completing this challenge will solidify your understanding of directive lifecycle hooks and template rendering.
Problem Description
You need to create a custom Angular directive called CustomFor that iterates over an array and renders a template for each item in the array. The directive should be usable in Angular templates similar to the built-in ngFor. The directive will receive an array as input and a template to repeat. It should dynamically create DOM elements based on the array's contents.
Key Requirements:
- Input Array: The directive must accept an array as input, which will be the data to iterate over. This input should be named
data. - Template: The directive must accept a template to be repeated for each item in the array. This template will be provided as a
TemplateRef. - Index: The directive should provide an
indexvariable within the template, representing the current iteration's index (starting from 0). - Dynamic Element Creation: The directive should dynamically create DOM elements based on the provided template and data.
- ViewContainerRef: Use
ViewContainerRefto add the rendered templates to the DOM. - No External Libraries: The solution should only use Angular's built-in features.
Expected Behavior:
When the CustomFor directive is used in a template, it should:
- Receive the
dataarray andtemplateas inputs. - Iterate over the
dataarray. - For each item in the array, create a view instance using the
templateandViewContainerRef. - Pass the current item and its index to the view context.
- Append the created view to the DOM.
Edge Cases to Consider:
- Empty Array: If the input array is empty, the directive should not render any elements.
- Null or Undefined Array: Handle cases where the input array is
nullorundefinedgracefully (e.g., by not rendering anything). - TemplateRef is Null/Undefined: Handle cases where the
TemplateRefis null or undefined.
Examples
Example 1:
Input:
data = [1, 2, 3]
template: <span>Item: {{ item }}, Index: {{ index }}</span>
Output:
<span>Item: 1, Index: 0</span>
<span>Item: 2, Index: 1</span>
<span>Item: 3, Index: 2</span>
Explanation: The directive iterates over the array [1, 2, 3]. For each number, it creates a span element with the provided template, substituting the `item` and `index` variables.
Example 2:
Input:
data = ['apple', 'banana', 'cherry']
template: <div class="fruit">{{ item }} - {{ index }}</div>
Output:
<div class="fruit">apple - 0</div>
<div class="fruit">banana - 1</div>
<div class="fruit">cherry - 2</div>
Explanation: The directive iterates over the array ['apple', 'banana', 'cherry']. For each fruit, it creates a div element with the provided template, substituting the `item` and `index` variables.
Example 3: (Edge Case - Empty Array)
Input:
data = []
template: <span>Item: {{ item }}, Index: {{ index }}</span>
Output:
(No output - no elements rendered)
Explanation: The directive receives an empty array. Since there are no items to iterate over, no elements are rendered.
Constraints
- Directive Structure: The directive must be a standard Angular directive (using
@Directive). - Input Types: The
datainput must be of typeArray<any>. - Performance: While not a primary focus, avoid unnecessary DOM manipulations. The solution should be reasonably performant for arrays of up to 100 elements.
- Angular Version: Assume Angular version 14 or higher.
Notes
- You'll need to use Angular's dependency injection to access
ViewContainerRefandTemplateRef. - The
ngFordirective usesTrackByFnfor change detection optimization. For simplicity, this challenge does not require implementingTrackByFn. - Focus on the core logic of iterating over the array and rendering the template. Styling and complex template logic are outside the scope of this challenge.
- Consider using
*ngIfwithin the template to conditionally render elements based on theindexif needed.