Angular Style Optimization Challenge
This challenge focuses on optimizing the styling of an Angular application to improve performance and maintainability. You will implement a system to dynamically apply and remove CSS classes based on component state and user interactions, ensuring efficient rendering and a clean codebase. This is crucial for creating responsive and performant user interfaces.
Problem Description
Your task is to build a reusable Angular directive that allows components to dynamically apply and remove a specified CSS class. The directive should toggle the class based on a boolean input property. This will enable components to manage their visual states efficiently without directly manipulating the DOM's class list.
Key Requirements:
- Create a custom Angular directive named
StyleTogglerDirective. - The directive should accept a boolean input property, let's call it
applyClass. - When
applyClassistrue, the directive should add a specified CSS class to the host element. - When
applyClassisfalse, the directive should remove the specified CSS class from the host element. - The directive needs to accept the name of the CSS class to be toggled as another input property, let's call it
toggleClass. - The directive should handle initial rendering correctly, applying the class if
applyClassis true on component initialization. - The directive should efficiently update the host element's classes when the
applyClassinput property changes.
Expected Behavior:
An element with the StyleTogglerDirective will have its specified CSS class added or removed solely based on the value of the applyClass input property.
Edge Cases:
- What happens if
toggleClassis not provided? The directive should ideally not throw an error and gracefully handle this situation (e.g., by not applying any class). - What if
applyClassis initiallytrue? The class should be applied upon directive initialization.
Examples
Example 1:
Scenario: A button that changes its appearance when clicked.
Component's TS:
import { Component } from '@angular/core';
@Component({
selector: 'app-my-button',
template: `
<button
[applyClass]="isActive"
toggleClass="active-button"
style="padding: 10px; border: 1px solid black;"
>
Click Me
</button>
`,
styles: [`
.active-button {
background-color: lightgreen;
font-weight: bold;
}
`]
})
export class MyButtonComponent {
isActive = false;
toggleActive() {
this.isActive = !this.isActive;
}
}
Explanation: The button element has the StyleTogglerDirective. Initially, isActive is false, so the active-button class is not applied. When the user interacts (e.g., clicks the button, triggering toggleActive), isActive becomes true, and the active-button class is added to the button element. The directive manages this class addition/removal.
Example 2:
Scenario: Highlighting an invalid form input.
Component's TS:
import { Component } from '@angular/core';
@Component({
selector: 'app-input-field',
template: `
<input
type="text"
[applyClass]="isInvalid"
toggleClass="error-input"
style="border: 1px solid grey; padding: 5px;"
/>
<p *ngIf="isInvalid" style="color: red;">Please enter a valid value.</p>
`,
styles: [`
.error-input {
border-color: red !important;
}
`]
})
export class InputFieldComponent {
isInvalid = true; // Assume input is initially invalid
}
Explanation: The input element uses StyleTogglerDirective. isInvalid is true on initialization, so the error-input class is applied, changing the input's border color to red. If isInvalid were to become false (e.g., after user correction), the error-input class would be removed.
Example 3:
Scenario: Using the directive without specifying toggleClass.
Component's TS:
import { Component } from '@angular/core';
@Component({
selector: 'app-no-class',
template: `
<div [applyClass]="showHighlight" style="padding: 10px; background-color: yellow;">
This div might be highlighted.
</div>
`,
styles: [`
/* No specific styles defined for a class name as it's not provided */
`]
})
export class NoClassComponent {
showHighlight = true;
}
Explanation: The div has applyClass set to true. However, since toggleClass is not provided, the directive will not attempt to add or remove any specific class. The div will retain its default styling. This demonstrates graceful handling of missing input.
Constraints
- The directive must be implemented as a standalone directive or within a module.
- The directive should not directly manipulate the DOM using
ElementRefif a more Angular-idiomatic approach (likeRenderer2) is available. - The solution should be written in TypeScript.
- Performance is important: avoid unnecessary DOM manipulations. The directive should only update the class list when
applyClassortoggleClasschanges.
Notes
- Consider using
OnChangeslifecycle hook to react to input property changes. Renderer2is a good tool for manipulating the DOM in a platform-agnostic way within Angular.- Think about how to handle the directive's initial state.
- A clean implementation will be testable and easy to integrate into other components.