Yazılım Mimarileri ve Tasarım Desenleri Üzerine

Angular 5 – Custom Structural Directives

angular5

Merhaba,

Bir önceki Angular 5 – Custom Directives başlıklı yazımda Angular 5 mimarisinde Custom Directives yapılarının nasıl oluşturulduğunu ve nasıl kullanıldığını detaylıca incelemiş bulunmaktayız. Bu içeriğimizde ise bir önceki makalede atılan temellerin üzerine Custom Structural Directives(Özel Yapısal Direktifler) yapılarını ekleyecek ve tam teferruatıyla incelemede ve değerlendirmede bulunacağız.

Yapısal direktiflerde TemplateRef ve ViewContainerRef olmak üzere iki modül ile karşılaşılmaktadır. Peki bu modüller nedir? ne iş yaparlar? sorusunu cevaplayalım…

İçeriğimizi örneklendirmek için “if”, “loop” ve “each” olmak üzere üç farklı adet işlevsellik üzerine yapısal direktif oluşturacağız.

Şimdi ise oluşturduğumuz özel yapısal direktifimizi kullanırken verilen yahut kaynakta bulunan değerleri kullanmayı, listelemeyi vs. inceleyelim.

İlk olarak each döngüsüne verilen kaynaktaki verileri listeleyelim.

import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';
import { isNgTemplate } from '../../../node_modules/@angular/compiler';

@Directive({
  selector: '[CustomFor]'
})
export class CustomForDirective {
  constructor
    (
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef
    ) { }

  @Input() set CustomFor(source: Array<any>) {
    source.forEach((item, index) => {
      this.viewContainerRef.createEmbeddedView(this.templateRef, {
        result: `${++index}-) ${item}`
      });
    });
  }
}

Gördüğünüz üzere “CustomFor” metodu içerisinde ViewContainerRef nesnesinin “createEmbededView” fonksiyonuna verilen TemplateRef nesnesinin haricinde, ikinci parametre olarak bir obje tanımlanmakta ve içerisine belirtilen “result” alanına döngüdeki her bir elemanı index değeriyle birlikte atıyor ve görüntüye ekliyoruz.

Kullanıma gelirsek eğer;

<div *CustomFor="source;let key=result">
  <p>{{key}}</p>
</div>

yukarıda görüldüğü üzere direktifimizi çağırdıktan sonra değer olarak noktalı virgüle(;) kadar döngünün kaynağını ifade edecek olan metodun parametresini veriyoruz, noktalı virgülden sonra “let” keywordüyle bir değişken tanımlayarak direktifte tanımladığımız “result” alanındaki her bir değeri bu değişkene atıyoruz. “source” değişkenine de aşağıdaki gibi “AppComponent” sınıfı içerisinden verimizi gönderiyoruz.

import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  source: Array<any> = ["a", "b", "c"];
}

Gördüğünüz üzere özel yapısal direktifimiz aracılığıyla bir kaynaktaki verileri listeletmiş bulunmaktayız.

Eğer ki, direktifi kullanırken “result” değerini otomatik karşılamak isterseniz “$implicit” operatörünü aşağıdaki gibi kullanabilirsiniz.

import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';
import { isNgTemplate } from '../../../node_modules/@angular/compiler';

@Directive({
  selector: '[CustomFor]'
})
export class CustomForDirective {
  constructor
    (
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef
    ) { }

  @Input() set CustomFor(source: Array<any>) {
    source.forEach((item, index) => {
      this.viewContainerRef.createEmbeddedView(this.templateRef, {
        $implicit: `${++index}-) ${item}`
      });
    });
  }
}

“$implicit” operatörü ilgili direktifteki değerleri direkt olarak viewe taşıma görevi gören bir operatördür.

<div *CustomFor="source;let key">
  <p>{{key}}</p>
</div>

“$implicit” operatörü ile veriler taşınıyorsa yukarıdaki gibi sadece tanımlanan değişken direkt olarak o değeri yakalayacaktır. Dolayısıyla bu kullanımda yine aynı çıktıyla karşılaşacağız.

Eğer ki birden fazla değer döndürmek ve viewde yakalamak istiyorsanız;

import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';
import { isNgTemplate } from '../../../node_modules/@angular/compiler';

@Directive({
  selector: '[CustomFor]'
})
export class CustomForDirective {
  constructor
    (
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef
    ) { }

  @Input() set CustomFor(source: Array<any>) {
    source.forEach((item, index) => {
      this.viewContainerRef.createEmbeddedView(this.templateRef, {
        resultValue: item,
        resultIndex: ++index
      });
    });
  }
}
<div *CustomFor="source;let value=resultValue;let index=resultIndex">
  <p>{{index}}-) {{value}}</p>
</div>

şeklinde çalışabilirsiniz.

Son olarak konuyla alakalı aşağıdaki örneğide incelemenizi tavsiye ediyorum.

import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';

@Directive({
  selector: '[CustomFor]'
})
export class CustomForDirective {
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef
  ) { }

  @Input() set CustomFor(num: number) {
    for (let i = 1; i <= num; i++)
      this.viewContainerRef.createEmbeddedView(this.templateRef, {
        $implicit: i != num ? `${i},` : `${i}`
      });
  }
}
<div>
  <span *CustomFor="20; let value">{{value}}</span>
</div>

İçeriğimizin sonuna gelmişken konumuzun genel kültürüyle ilgili vurgulamak istediğim son bir husus mevcuttur;

Dikkat ederseniz özel olarak oluşturulmuş direktifler kullanılırken direktif adının başına “*” karakteri verilmektedir. Bu karakter okunabilir kodu yahut anlaşılabilir yapısallığı ifaden eden syntactic sugar olarak nitelendirilmektedir. “*” karakteri ile direktif çağrıldığında ilgili dom elementi kapsadığı tüm yapılarla birlikte tamamen ele alınmakta ve TemplateRef nesnesi aracılığıyla ilgili element istenildiği gibi modifike edilebilmektedir.

İlgilenenlerin faydalanması dileğiyle…

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Mobil sürümden çık