What are Directives in Angular?
Question 1: What are Directives in Angular and what are their types?
Answer: Directives in Angular are classes that add behavior to elements in your applications. There are three types:
- Component Directives: Components are directives with a template
 - Structural Directives: Change the DOM layout by adding/removing elements
 - Attribute Directives: Change the appearance or behavior of an element
 
Example of each type:
// 1. Component Directive
@Component({
  selector: 'app-user',
  template: `<h1>{{name}}</h1>`
})
class UserComponent {
  name = 'John';
}
// 2. Structural Directive
@Directive({
  selector: '[appUnless]'
})
class UnlessDirective {
  @Input() set appUnless(condition: boolean) {
    if (!condition) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    } else {
      this.viewContainer.clear();
    }
  }
  
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef
  ) {}
}
// 3. Attribute Directive
@Directive({
  selector: '[appHighlight]'
})
class HighlightDirective {
  @Input() highlightColor = 'yellow';
  
  @HostListener('mouseenter')
  onMouseEnter() {
    this.highlight(this.highlightColor);
  }
  
  @HostListener('mouseleave')
  onMouseLeave() {
    this.highlight(null);
  }
  
  private highlight(color: string | null) {
    this.el.nativeElement.style.backgroundColor = color;
  }
  
  constructor(private el: ElementRef) {}
}Question 2: How do you create and use Custom Directives in Angular?
Answer: Custom directives are created using the @Directive decorator and can interact with the host element through ElementRef, HostListener, and HostBinding.
// Custom attribute directive
@Directive({
  selector: '[appTooltip]',
  standalone: true
})
class TooltipDirective {
  @Input('appTooltip') tooltipText = '';
  
  // Host element binding
  @HostBinding('style.position') position = 'relative';
  
  // Host element event listener
  @HostListener('mouseenter')
  showTooltip() {
    this.tooltip.style.display = 'block';
  }
  
  @HostListener('mouseleave')
  hideTooltip() {
    this.tooltip.style.display = 'none';
  }
  
  private tooltip: HTMLElement;
  
  constructor(private el: ElementRef) {
    this.createTooltip();
  }
  
  private createTooltip() {
    this.tooltip = document.createElement('div');
    this.tooltip.className = 'tooltip';
    this.tooltip.textContent = this.tooltipText;
    this.el.nativeElement.appendChild(this.tooltip);
  }
}
// Using the directive
@Component({
  template: `
    <button [appTooltip]="'Click me!'">
      Hover me
    </button>
  `
})
class AppComponent { }Question 3: How do Structural Directives work in Angular?
Answer: Structural directives manipulate DOM elements using microsyntax (*directive) and work with TemplateRef and ViewContainerRef to add/remove elements.
// Custom structural directive
@Directive({
  selector: '[appRepeat]'
})
class RepeatDirective {
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef
  ) {}
  
  @Input() set appRepeatTimes(count: number) {
    this.viewContainer.clear();
    for (let i = 0; i < count; i++) {
      this.viewContainer.createEmbeddedView(
        this.templateRef,
        { index: i }
      );
    }
  }
}
// Using structural directive
@Component({
  template: `
    <!-- Repeat element 3 times -->
    <div *appRepeat="3; let i=index">
      Item {{i}}
    </div>
    
    <!-- Built-in structural directives -->
    <div *ngIf="show">Conditional</div>
    <div *ngFor="let item of items">{{item}}</div>
    <div [ngSwitch]="value">
      <div *ngSwitchCase="'A'">A</div>
      <div *ngSwitchDefault>Default</div>
    </div>
  `
})
class AppComponent { }Question 4: How do you share data between directives and components?
Answer: Data can be shared using Input/Output decorators, services, or by accessing the host component through dependency injection.
// Directive with inputs and outputs
@Directive({
  selector: '[appInteractive]'
})
class InteractiveDirective {
  @Input() config: any;
  @Output() action = new EventEmitter<string>();
  
  @HostListener('click')
  onClick() {
    this.action.emit('clicked');
  }
}
// Service for sharing data
@Injectable()
class SharedService {
  private data = new BehaviorSubject<any>(null);
  data$ = this.data.asObservable();
  
  updateData(value: any) {
    this.data.next(value);
  }
}
// Component using directive and service
@Component({
  template: `
    <div [appInteractive]="config"
         (action)="handleAction($event)">
      Interactive Element
    </div>
  `
})
class AppComponent {
  constructor(private shared: SharedService) {}
  
  handleAction(event: string) {
    this.shared.updateData(event);
  }
}Question 5: What are the lifecycle hooks available for directives?
Answer: Directives have access to the same lifecycle hooks as components, such as OnInit, OnDestroy, OnChanges, etc.
@Directive({
  selector: '[appLifecycle]'
})
class LifecycleDirective implements OnInit, 
  OnDestroy, OnChanges {
  @Input() config: any;
  
  ngOnInit() {
    console.log('Directive initialized');
  }
  
  ngOnChanges(changes: SimpleChanges) {
    if (changes['config']) {
      console.log('Config changed:', 
        changes['config'].currentValue);
    }
  }
  
  ngOnDestroy() {
    console.log('Directive destroyed');
  }
}Common Interview Follow-up Questions:
Q: What’s the difference between HostBinding and ElementRef? A: HostBinding provides declarative binding to host element properties, while ElementRef gives direct access to the DOM element:
@Directive({ selector: '[appStyle]' }) class StyleDirective { // Declarative binding @HostBinding('style.color') color = 'red'; // Direct DOM access constructor(private el: ElementRef) { el.nativeElement.style.fontSize = '16px'; } }Q: How do you optimize directive performance? A: Use OnPush change detection, avoid expensive operations in lifecycle hooks, and implement pure pipes for transformations:
@Directive({ selector: '[appOptimized]', changeDetection: ChangeDetectionStrategy.OnPush }) class OptimizedDirective { @Input() set data(value: any) { // Cache expensive computations if (this.cached !== value) { this.cached = value; this.compute(); } } }Q: How do you test directives? A: Test directives using TestBed and ComponentFixture:
describe('HighlightDirective', () => { let component: TestComponent; let fixture: ComponentFixture<TestComponent>; beforeEach(() => { TestBed.configureTestingModule({ declarations: [ TestComponent, HighlightDirective ] }); fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; }); it('should highlight on hover', () => { const element = fixture.debugElement .query(By.directive(HighlightDirective)); element.triggerEventHandler('mouseenter', null); expect(element.nativeElement.style.backgroundColor) .toBe('yellow'); }); });Q: What are the best practices for creating directives? A: Follow these guidelines:
- Use meaningful selectors with prefixes
 - Keep directives focused and single-responsibility
 - Provide good documentation
 - Make them reusable and configurable
 
@Directive({ selector: '[appPrefix]', standalone: true }) class WellDesignedDirective { // Clear input names @Input() config: DirectiveConfig; // Document public methods /** * Updates the directive's state * @param newState The new state to apply */ updateState(newState: State) { // Implementation } }
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.