Hone logo
Hone
Problems

Custom ngClass Directive Implementation

This challenge asks you to create your own version of Angular's built-in ngClass directive. This directive is fundamental for dynamically adding and removing CSS classes from HTML elements based on the state of your application, making UI styling flexible and responsive.

Problem Description

Your task is to build a custom Angular directive named appNgClass that replicates the core functionality of the [ngClass] directive. This directive should allow you to bind a string, an array of strings, or an object where keys are class names and values are booleans to an element's class attribute.

Key Requirements:

  1. Directive Name: The directive must be named appNgClass.
  2. Input Binding: It should accept an input property that can be one of the following:
    • A string: e.g., "my-class another-class"
    • An array of strings: e.g., ['my-class', 'another-class']
    • An object: e.g., { 'my-class': true, 'another-class': false }
  3. Dynamic Class Application: The directive should dynamically add or remove CSS classes from the host element based on the provided input.
    • If a class name is present in a string or array, or if its corresponding value is true in an object, it should be added.
    • If a class name is not present or its corresponding value is false, it should be removed.
  4. Previous Class Management: The directive must correctly handle updates to the input. When the input changes, any classes that were previously applied by the directive but are no longer specified should be removed.
  5. Idempotency: Applying the directive multiple times with the same class names should not cause issues or duplicate classes.
  6. Element Ref: You will need to access the host element using ElementRef.

Expected Behavior:

When the appNgClass directive is applied to an HTML element, the specified CSS classes should be added or removed from the element's class attribute according to the logic described above.

Edge Cases to Consider:

  • Empty Inputs: What happens when the input is an empty string, an empty array, or an empty object?
  • Input Type Changes: What if the input type changes dynamically (e.g., from a string to an object)?
  • Null/Undefined Inputs: How should the directive behave if the input is null or undefined?
  • Interactions with Existing Classes: The directive should only manage the classes it's responsible for. It should not interfere with classes already present on the element or added by other means.

Examples

Example 1: String Input

<div [appNgClass]="'highlight warning'">This div will have 'highlight' and 'warning' classes.</div>

Output: The div element will have the classes highlight and warning applied.

Explanation: The string "highlight warning" is parsed, and both highlight and warning classes are added to the element.

Example 2: Array Input

<p [appNgClass]="['active', 'large']">This paragraph will have 'active' and 'large' classes.</p>

Output: The p element will have the classes active and large applied.

Explanation: The array ['active', 'large'] is processed, and both active and large classes are added to the element.

Example 3: Object Input with Dynamic Updates

Consider a component with a property: isActive = true; isError = false;

And the following template:

<span [appNgClass]="{ active: isActive, error: isError, special: true }">
  This span's classes will change.
</span>

Initial State:

  • isActive is true.
  • isError is false.

Output: The span element will have classes active and special.

Explanation: The object { active: true, error: false, special: true } is evaluated. active and special are added because their values are true. error is not added because its value is false.

Later State: If isActive becomes false and isError becomes true.

Output: The span element will have classes error and special.

Explanation: The directive re-evaluates the object. active is removed because isActive is now false. error is added because isError is now true. special remains because its value is still true.

Example 4: Handling Input Changes and Removing Old Classes

Consider an element with the directive:

<div [appNgClass]="currentClasses">...</div>

And in the component:

currentClasses = ['classA', 'classB'];

// Later, currentClasses changes to:
// currentClasses = ['classB', 'classC'];

Initial State: The div will have classA and classB.

After Update: The div should have classB and classC. classA should be removed.

Explanation: The directive must track the classes it previously applied. When the input changes, it should remove any classes that are no longer specified in the new input.

Constraints

  • The directive must be implemented as a standalone directive or as part of an Angular module.
  • The solution should be written in TypeScript.
  • Do not use any third-party libraries or external DOM manipulation utilities beyond standard Angular APIs (ElementRef, Renderer2, OnInit, OnChanges).
  • The solution should be efficient and avoid unnecessary DOM manipulations.

Notes

  • Consider using Renderer2 for manipulating the DOM safely and effectively.
  • The OnChanges lifecycle hook will be crucial for detecting when the input property changes.
  • You'll need to parse the input string and array to identify individual class names.
  • Think about how to store the set of classes that the directive has applied to the element so you can manage their removal upon updates.
  • The directive should not modify classes that were not applied by itself.
Loading editor...
typescript