Angular custom decorator

Angular custom decorator

Merhaba,

Bu yazımda Angular da işimize yarayacak decorator kullanımından bahsedeceğim. Bu kullanımı anlatabilmek için bir component oluşturalım ve bu componentin input olarak aldığı parametrelerin zorunlu olması durumunu kontrol edelim.

Component oluşturma

İlk olarak projemizde aşağıdaki komut ile greeter componentimizi oluşturalım.

ng g c greeter

Component hazır olduğuna göre özelleştirme işlemini yapalım ve bu componentimiz basit olarak verilen bir isim için karşılama mesajı versin. Gerekli düzenlemeleri aşağıdaki gibi yapalım.

greeter.component.html içeriği

<h2>Hello, {{ name }}</h2>
<h3>{{ message }}</h3>

greeter.component.ts içeriği

import { Component, Input, VERSION } from '@angular/core';

@Component({
  selector: 'app-greeter',
  templateUrl: './greeter.component.html',
  styleUrls: ['./greeter.component.css'],
})
export class GreeterComponent {
  @Input() public name: string;
  @Input() public message: string;
}

Görüldüğü gibi basit bir component. Şimdi name ve message değerlerini zorunlu yapmak istediğimizde iki seçenek kullanabiliriz. Birincisi selector tanımında bunu belirtmek ya da bir decorator yardımı ile bunu belirtmek. Bu konumuzun özelinde decorator oluşturalım ve onu kullanalım, ayrıca selector tanımında belirttiğimiz zorunluluğun bizim için çok işlevsel olmadığınıda görelim.

Decorator oluşturma

Decorator oluşturmak için projemizde bir decorators isim klasör açıp içerisinde de required.decorator.ts isimli bir dosya açıp fonksiyonumuzu yazalım.

required.decorator.ts içeriği

export function Required(target: object, propertyKey: string) {
  Object.defineProperty(target, propertyKey, {
    get() {
      throw new Error(`Attribute '${propertyKey}' is required.`);
    },
    set(value) {
      Object.defineProperty(target, propertyKey, {
        value,
        writable: true,
        configurable: true,
      });
    },
    configurable: true,
  });
}

Şimdi yeni hazırladığımız bu require decorator tanımımızı componentimizde kullanalım. Gerekli düzenleme sonrası içeriğimiz aşağıdaki gibi olacaktır.

greeter.component.ts içeriği @Required kullanımı ile

import { Component, Input, VERSION } from '@angular/core';
import { Required } from '../decorators/required.decorator';

@Component({
  selector: 'app-greeter',
  templateUrl: './greeter.component.html',
  styleUrls: ['./greeter.component.css'],
})
export class GreeterComponent {
  @Input() @Required public name: string;
  @Input() public message: string;
}

Bu düzenlemeden sonra yeni componentimizi kullanalım ve bize sağladığını zorunluluk kontrolünün sonucunu görelim. Bunun için app.component.html içerisinde aşağıdaki düzenlemeyi yapalım.

<h1>{{ title }}</h1>
<hr />
<app-greeter name="Samet" message="Custom decorator"></app-greeter>

Bu kullanım sonucu herhangi bir hata bulunmuyor ve ekran çıktımız aşağıdaki gibi oluyor.

image.png

Artık sıra zorunlu olarak belirtiğimiz alana değer vermeden kullanmaya geldi ve hazırlamış olduğumuz decorator tarafından da uyarı mesajımızı göreceğiz. Kodumuzu aşağıdaki gibi düzeltelim ve hatamızı görelim.

<h1>{{ title }}</h1>
<hr />
<app-greeter message="Custom decorator"></app-greeter>

image.png

Hata mesajında da görüldüğü gibi bu tarz bir decorator ile istediğimiz gibi özelleştirme yapabiliyoruz. Şimdi de decorator olmadan selector tanımında bir zorunluluk kontrolü yapalım ve onun sonucunu görelim. Bu işlem için kodumuzu aşağıdaki gibi değiştirelim sonra da kullanımına bakalım. Selector tanımında message bilgisini zorunlu olarak tanımladık

greeter.component.ts içeriği selector düzenleme kullanımı ile

import { Component, Input, VERSION } from '@angular/core';
import { Required } from '../decorators/required.decorator';

@Component({
  selector: 'app-greeter[message]',
  templateUrl: './greeter.component.html',
  styleUrls: ['./greeter.component.css'],
})
export class GreeterComponent {
  @Input() public name: string;
  @Input() public message: string;
}

Son olarak kullanımımızda message bilgisini kullanmayalım ve sonuçlarını görelim.

<h1>{{ title }}</h1>
<hr />
<app-greeter name="Samet"></app-greeter>

image.png

Yukarıdaki gibi selector yardımı ile bir alanı zorunlu yaptığımızda aldığımız hata mesajında net olarak hangi alan için bu hatayı verdiğini bulmamız zor, ama bunun yerine kendi hazırlayacağımız custom decorator ile yukarıda yaptığımız required decoratoru gibi net olarak hangi alanda hata var bunu görebiliyoruz.

Ayrıca işlemlerimizi loglamak içinde bir decorator oluşturup onu kullanabiliriz.

log.decorator.ts içeriği

export function Log(params?: { color?: string }): MethodDecorator {
  return function (target: Function, propertyKey: string, descriptor: any) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      console.log(
        `%c |-> Entering '${propertyKey}' method`,
        params ? `color:${params.color}` : ''
      );
      console.log(
        `%c "${propertyKey}" method args:${args}`,
        'background:green;color:white'
      );
      const result = originalMethod.apply(this, args);
      console.log(
        `%c <-| Leaving '${propertyKey}' method`,
        params ? `color:${params.color}` : ''
      );

      return result;
    };

    return descriptor;
  };
}

Yeni oluşturduğumuz be decorator ile istediğimiz method için log kaydı alabiliriz. Örnek kullanımı için app.component.ts içeriğine göz atalım.

app.component.ts içeriği

import { Component, OnInit, VERSION } from '@angular/core';
import { Log } from './decorators/log.decorator';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  public title = `Angular ${VERSION.major} Custom Decorator`;

  @Log()
  public ngOnInit(): void {
    this.sayHi('Hi', 'Samet');
  }

  @Log({ color: 'red' })
  public sayHi(message: string, name: string): void {
    console.log(message, name);
  }
}

Bu kullanım sonrası çıktımız aşağıdaki gibi olacaktır.

image.png

Sizde kendi ihtiyaçlarınıza göre bu tarz decorator yazıp işinizi kolaylaştırabilirsiniz. Konuya ait GitHub ve StackBlitz aşağıdadır.