You can create a 

@Directive()

for that!

Adithya Sreyaj

Front-end @ Hypersonix

@Directive()

Directives are classes that add additional behavior to elements in your Angular applications.

  1. Components - directives with a template.
  2. Attribute directives - change the appearance or behavior of an element, component, or another directive.
  3. Structural directives - change the DOM layout by adding and removing DOM elements.

Built-in Directives

  • NgClass
  • NgStyle
  • NgModel

Attribute Directives

  • NgIf
  • NgFor
  • NgSwitch

Structural Directives

Possibilities

  1. Access to the native element
  2. Access to template reference
  3. Listen and Handle events
  4. Input Validations

Benefits

  1. Reusable
  2. Maintainable
  3. Testable
  4. Lean Components

Directives in Action

Delta Value Directive

  • Attribute directive to add classes based on some condition.

  • Takes a number as input

  • Checks if the number is positive or negative

  • Just adds a class to the host element

  • Add arrow based on the change

@Directive({
  selector: '[deltaColorArrow]',
})
export class DeltaColorArrowDirective implements OnChanges {
  @Input() value!: number;

  private arrow!: HTMLElement;

  @HostBinding('class')
  get classes() {
    return this.value > 0 ? 'positive' : 'negative';
  }
  constructor(@Inject(DOCUMENT) private document: Document, private el: ElementRef) {}

  ngOnChanges(changes: SimpleChanges): void {
    const currentValue = changes.value?.currentValue;
    if (currentValue != undefined) {
      const el = this.el.nativeElement as HTMLElement;
      if (!this.arrow) {
        this.arrow = this.getArrowElement(currentValue);
        el.appendChild(this.arrow);
      } else {
        this.arrow.textContent = currentValue > 0 ? '⮝' : '⮟';
      }
    }
  }

  getArrowElement(value: number) {
    const arrow = this.document.createElement('span');
    arrow.style.setProperty('margin-left', '4px');
    arrow.textContent = value > 0 ? '⮝' : '⮟';
    return arrow;
  }
}
<p class="negative">
  -5.6%
  <span style="margin-left: 4px;">⮟</span>
</p>
 <p deltaColorArrow [value]="-5.6">-5.6%</p>

Fullscreen Directive

  • Attribute directive

  • Exports the directive to be used in a template

  • Manage state inside the directive

@Directive({
  selector: '[appUiFullscreen]',
  exportAs: 'fullscreen',
})
export class UiFullscreenDirective {
  private isMaximizedSubject = new BehaviorSubject(false);
  isMaximized$ = this.isMaximizedSubject.asObservable();

  constructor(private el: ElementRef) {}

  toggle() {
    if (this.isMaximizedSubject?.getValue()) this.minimize();
    else this.maximize();
  }
  maximize() {
    if (this.el) {
      this.isMaximizedSubject.next(true);
      this.nativeElement.classList.add('fullscreen');
       if (Fullscreen.isEnabled) {
        Fullscreen.request();
      }
    }
  }
  minimize() {
    if (this.el) {
      this.isMaximizedSubject.next(false);
      this.nativeElement.classList.remove('fullscreen');
      if (Fullscreen.isEnabled) {
        Fullscreen.exit();
      }
    }
  }

  private get nativeElement() {
    return this.el.nativeElement as HTMLElement;
  }
}
<div appUiFullscreen #fullscreen="fullscreen">
  <header>
    <p>Total Sales Report</p>
    <div>
      <button (click)="fullscreen.minimize()">🗕</button>
      <button (click)="fullscreen.maximize()">🗖</button>
    </div>
  </header>
  <div class="body"></div>
</div>
.fullscreen {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
}

Permission Directive

  • Structural directive

  • Displays content only if authorized

  • Pass feature and permission as input

  • Validates the permission and embed the item in view

@Directive({
  selector: '[appUiPermissions]',
})
export class UiPermissionsDirective implements OnInit, OnDestroy {
  private loggedInUser!: User;
  private permission!: Permissions;
  private feature!: string;

  private subscription!: Subscription;

  @Input()
  set appUiPermissions(permission: Permissions) {
    this.permission = permission;
    this.updateView();
  }

  @Input()
  set appUiPermissionsFeature(feature: string) {
    this.feature = feature;
    this.updateView();
  }

  constructor(private tpl: TemplateRef<any>, 
              private vcr: ViewContainerRef, 
              private authService: AuthService) {}

  ngOnInit() {
    this.subscription = this.authService.loggedUser$.subscribe((user) => {
      this.loggedInUser = user;
      this.updateView();
    });
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

  private updateView() {
    this.vcr.clear();
    if (this.hasPermission()) {
      this.vcr.createEmbeddedView(this.tpl);
    } else {
      this.vcr.clear();
    }
  }

  private hasPermission() {
    if (!this.loggedInUser) return false;
    const featurePermissions = this.loggedInUser.permissions[this.feature];
    if (featurePermissions) {
      return featurePermissions.includes(this.permission);
    }
    return false;
  }
}
<footer>
  <button *appUiPermissions="'READ'; feature: 'product'">
    View
  </button>
  <button *appUiPermissions="'UPDATE'; feature: 'product'">
    Edit
  </button>
  <button *appUiPermissions="'DELETE'; feature: 'product'">
    Delete
  </button>
</footer>
export enum Permissions {
  create = 'CREATE',
  read = 'READ',
  update = 'UPDATE',
  delete = 'DELETE',
}

const permissions = {
    product: [
      'CREATE',
      'READ',
      'UPDATE'
      'DELETE' 
    ]
 }

Demo & Code

Made with Slides.com