<repo_name>offwork/custom-directive-pipe<file_sep>/src/app/custom-pipes/order-by.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'orderBy' })
export class OrderByPipe implements PipeTransform {
  transform(value: any | any[], key: string): any[] {
    if (!Array.isArray(value)) {
      return;
    }
    value.sort((a: any, b: any) => {
      if (a[key] < b[key]) {
        return -1;
      } else if (a[key] > b[key]) {
        return 1;
      } else {
        return 0;
      }
    });

    return value;
  }
}
<file_sep>/src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { TextHighlightDirective } from './custom-directives/attribute-directive/text-highlight.directive';

import { CustomForDirective } from './custom-directives/structural-directive/custom-structural.directive';

import { OrderByPipe } from './custom-pipes/order-by.pipe';

@NgModule({
  declarations: [
    CustomForDirective,
    TextHighlightDirective,
    OrderByPipe,
    AppComponent
  ],
  imports: [BrowserModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}
<file_sep>/src/app/custom-directives/attribute-directive/text-highlight.directive.ts
import {
  Directive,
  Input,
  ElementRef,
  Renderer2,
  OnChanges,
  SimpleChanges
} from '@angular/core';

@Directive({
  selector: '[appTextHighlight]'
})
export class TextHighlightDirective implements OnChanges {
  @Input('appTextHighlight') textToHighlight: string;
  @Input() color = 'yellow';

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {}

  ngOnChanges(changes: SimpleChanges): void {
    if (!this.elementRef.nativeElement.innerText) {
      return;
    }

    const innerText = this.elementRef.nativeElement.innerText;
    const markElement = `<mark style="background-color: ${this.color};">${
      this.textToHighlight
    }</mark>`;
    const html = innerText.split(this.textToHighlight).join(markElement);
    this.renderer.setProperty(this.elementRef.nativeElement, 'innerHTML', html);
  }
}
<file_sep>/src/app/app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  myCollection: any[] = [
    { birthDay: 'April 15, 1968', firstName: 'Haluk', lastName: 'Levent' },
    { birthDay: 'May 20, 1946', firstName: 'Müslüm', lastName: 'Gürses' },
    { birthDay: 'July 12, 1942', firstName: 'Kemal', lastName: 'Sunal' },
    { birthDay: 'Junuary 30, 1944', firstName: 'Hasan', lastName: 'Cemal' },
    { birthDay: 'March 31, 1453', firstName: 'Ajda', lastName: 'Pekkan' },
    { birthDay: 'September 10, 1990', firstName: 'Selma', lastName: 'Elma' }
  ];
  maxElements = 4;
  movieQuote = `
      The first rule of Fight Club is: You do not talk about Fight Club.
      The second rule of Fight Club is: You do not talk about Fight Club.
      Third rule of Fight Club: someone yells stop, goes limp, taps out, the fight is over.
      Fourth rule: only two guys to a fight. Fifth rule: one fight at a time, fellas.
      Sixth rule: no shirts, no shoes.
      Seventh rule: fights will go on as long as they have to.
      And the eighth and final rule: if this is your first night at Fight Club,
      you have to fight.
  `;
}
<file_sep>/src/app/custom-directives/structural-directive/custom-structural.directive.ts
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

interface RangeContext {
  $implicit: number;
  index: number;
  first: boolean;
  last: boolean;
}

@Directive({
  selector: '[appCustomFor]'
})
export class CustomForDirective {
  @Input()
  set appCustomFor(value: [number, number] | number) {
    this.viewContainerRef.clear();

    const [from, to] = Array.isArray(value) ? value : [0, value];
    const range = this.generateRange(from, to);

    range.forEach((itemNum, index) =>
      this.viewContainerRef.createEmbeddedView(this.templateRef, {
        $implicit: itemNum,
        index,
        first: index === 0,
        last: index + 1 === range.length
      })
    );
  }

  constructor(
    private templateRef: TemplateRef<RangeContext>,
    private viewContainerRef: ViewContainerRef
  ) {}

  private generateRange(from: number = 0, to: number): number[] {
    return Array.from({ length: to - from }, (_, index) => index + from);
  }
}
