Hone logo
Hone
Problems

Implementing a Custom @if Directive in Angular

Angular's template syntax is powerful and expressive. While it provides built-in directives like *ngIf for conditional rendering, understanding how such directives are built can deepen your grasp of Angular's View Engine and compilation process. This challenge asks you to create a custom directive that mimics the behavior of @if in Angular templates.

Problem Description

Your task is to implement a custom structural directive named customIf that behaves similarly to Angular's built-in *ngIf directive. This directive should conditionally render an embedded template based on a given condition.

Key Requirements:

  • Directive Creation: Create a new Angular structural directive with the selector [customIf].
  • Conditional Rendering: The directive should take a boolean expression as input. If the expression evaluates to true, the content of the template should be rendered. If it evaluates to false, the content should be removed from the DOM.
  • Template Handling: The directive needs to correctly interact with Angular's TemplateRef and ViewContainerRef to add or remove the embedded view.
  • else Block Support: Extend the directive to support an optional else block. This means the directive should accept a second input, typically named customIfElse, which is also a TemplateRef. If the primary customIf condition is false, the customIfElse template should be rendered instead.
  • Dynamic Updates: The directive should react to changes in the input condition and update the rendered template accordingly.

Expected Behavior:

  • When [customIf]="condition" is used and condition is true, the host element's template content will be displayed.
  • When [customIf]="condition" is used and condition is false, the host element's template content will be removed.
  • When [customIf]="condition" [customIfElse]="elseTemplate" is used and condition is true, the host element's template content will be displayed, and elseTemplate will not be rendered.
  • When [customIf]="condition" [customIfElse]="elseTemplate" is used and condition is false, the host element's template content will be removed, and elseTemplate will be rendered.

Edge Cases to Consider:

  • What happens if the else template is not provided?
  • How does the directive handle changes in the condition after initial rendering?
  • Ensure that the directive correctly cleans up views when they are no longer needed.

Examples

Example 1: Basic customIf

Component Template:

<div *customIf="showContent">
  This content is shown when showContent is true.
</div>

Component Class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  standalone: true,
})
export class MyComponent {
  showContent = true;
}

Expected Output (when showContent is true):

<div customif="">
  This content is shown when showContent is true.
</div>

Explanation: The showContent variable is true, so the div's content is rendered.

Example 2: Basic customIf with false condition

Component Template:

<div *customIf="showContent">
  This content is shown when showContent is true.
</div>

Component Class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  standalone: true,
})
export class MyComponent {
  showContent = false;
}

Expected Output (when showContent is false):

The div and its content are not rendered in the DOM.

Explanation: The showContent variable is false, so the div's content is removed.

Example 3: customIf with else block

Component Template:

<ng-container *customIf="isLoggedIn; else loggedOutTemplate">
  Welcome back, User!
</ng-container>

<ng-template #loggedOutTemplate>
  Please log in.
</ng-template>

Component Class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  standalone: true,
})
export class MyComponent {
  isLoggedIn = false;
}

Expected Output (when isLoggedIn is false):

<ng-container customif="" customifelse="">
  Please log in.
</ng-container>

Explanation: isLoggedIn is false, so the else template (loggedOutTemplate) is rendered. The primary content is not.

Example 4: customIf with else block and dynamic updates

Component Template:

<button (click)="toggleLogin()">Toggle Login</button>

<ng-container *customIf="isLoggedIn; else loggedOutTemplate">
  Welcome back, User!
</ng-container>

<ng-template #loggedOutTemplate>
  Please log in.
</ng-template>

Component Class:

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  standalone: true,
})
export class MyComponent {
  isLoggedIn = false;

  toggleLogin() {
    this.isLoggedIn = !this.isLoggedIn;
  }
}

Expected Behavior (after clicking the button twice, starting with isLoggedIn = false):

  1. Initial render (isLoggedIn = false): "Please log in."
  2. After first click (isLoggedIn = true): "Welcome back, User!"
  3. After second click (isLoggedIn = false): "Please log in."

Explanation: The directive dynamically updates the rendered template based on changes to the isLoggedIn property.

Constraints

  • The directive must be implemented as a structural directive.
  • The directive must use TemplateRef and ViewContainerRef for DOM manipulation.
  • The directive should be implemented in TypeScript.
  • The directive should be compatible with Angular's Ivy renderer.
  • The directive should not rely on any external libraries for its core functionality.

Notes

  • Structural directives are prefixed with an asterisk (*) in the template syntax, which is a syntactic sugar for passing the element's template as a TemplateRef to the directive.
  • Consider how to manage the TemplateRef for both the main content and the else content.
  • Think about when and how to create and destroy views using ViewContainerRef.
  • The ngOnChanges lifecycle hook might be useful for reacting to input property updates.
  • The else input property can be defined using @Input and named customIfElse.
Loading editor...
typescript