Hone logo
Hone
Problems

Dynamic List Renderer with Structural Directive

This challenge asks you to create a custom structural directive in Angular that dynamically renders a list of items based on an input array. Structural directives control the DOM by adding, removing, or manipulating elements. This is a fundamental concept for building reusable components and managing complex UI structures.

Problem Description

You need to build an Angular structural directive called ListRenderer that takes an array of data as input and renders each item in the array as an <li> element within an unordered list (<ul>). The directive should dynamically add or remove <li> elements from the <ul> based on changes to the input array. The directive should also accept an optional itemTemplate input, which is a template to be used for rendering each item. If no itemTemplate is provided, a simple text representation of the item should be used.

Key Requirements:

  • The directive must accept an array of data as input (@Input() data: any[]).
  • The directive must render each item in the array as an <li> element within a <ul> element.
  • The directive must dynamically update the list when the input array changes.
  • The directive should accept an optional template input (@Input() itemTemplate: TemplateRef<any>).
  • If itemTemplate is provided, it should be used to render each item.
  • If itemTemplate is not provided, the item's string representation (using toString()) should be displayed within the <li>.

Expected Behavior:

When the data input changes, the directive should:

  1. Remove any existing <li> elements that are no longer in the new data array.
  2. Add new <li> elements for any items that are now in the data array but were not previously rendered.
  3. Update the content of existing <li> elements if the item's data has changed.

Edge Cases to Consider:

  • Empty input array: The <ul> should be empty.
  • null or undefined input array: The <ul> should be empty.
  • Items in the array that are null or undefined: Handle these gracefully (e.g., display a placeholder or skip rendering).
  • itemTemplate is provided but invalid: Handle the error gracefully (e.g., fall back to the default rendering).

Examples

Example 1:

Input: data = [{name: 'Alice'}, {name: 'Bob'}]
Output:
<ul>
  <li><div class="item">Alice</div></li>
  <li><div class="item">Bob</div></li>
</ul>
Explanation: The directive renders two list items, each displaying the 'name' property of the corresponding object.  The default rendering is used as no template is provided.

Example 2:

Input: data = [{name: 'Alice'}, {name: 'Bob'}, {name: 'Charlie'}]
itemTemplate: <ng-template #itemTemplate let-item="item">
                <div class="custom-item">
                  <strong>{{item.name}}</strong> - {{item.age | default: 'N/A'}}
                </div>
              </ng-template>
Output:
<ul>
  <li><div class="custom-item"><strong>Alice</strong> - N/A</div></li>
  <li><div class="custom-item"><strong>Bob</strong> - N/A</div></li>
  <li><div class="custom-item"><strong>Charlie</strong> - N/A</div></li>
</ul>
Explanation: The directive renders three list items, each using the provided `itemTemplate` to display the 'name' and 'age' properties. The `default` pipe handles cases where 'age' is undefined.

Example 3:

Input: data = []
Output:
<ul></ul>
Explanation: The directive renders an empty unordered list because the input array is empty.

Constraints

  • The directive must be implemented using Angular's structural directive API (@Directive, @Input, ViewContainerRef, TemplateRef).
  • The directive should be performant and avoid unnecessary DOM manipulations.
  • The directive should be reusable and adaptable to different data structures.
  • The directive should handle potential errors gracefully.
  • The solution must be written in TypeScript.

Notes

  • Consider using ViewContainerRef.clear() to remove all existing elements before re-rendering the list.
  • Use ViewContainerRef.createEmbeddedView() to create views from the itemTemplate.
  • Think about how to efficiently compare items in the input array to determine if they need to be added, removed, or updated. Simple array comparison might be sufficient for smaller datasets, but consider more optimized approaches for larger datasets.
  • The default pipe is a placeholder. You can implement it or use a similar approach to handle missing data. The key is to demonstrate handling of potentially missing data within the template.
Loading editor...
typescript