Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Angular 5 – Custom Structural Directives

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…

  • TemplateRef
    Direktifin kullanıldığı template’in içindekileri içerir.
  • ViewContainerRef
    Template’in görünümünü tutar, dolayısıyla template içerisinde bulunan elemanları görüntünün içerisine yerleştirme yahut kaldırma işlevini yerine getirmektedir.

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

  • If Şartı İçin Özel Yapısal Direktif Oluşturma

    import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';
    
    @Directive({
      selector: '[CustomIf]'
    })
    export class CustomIfDirective {
      constructor
        (
        private templateRef: TemplateRef<any>,
        private viewContainerRef: ViewContainerRef
        ) { }
      @Input() set CustomIf(condition: boolean) {
        if (condition)
          this.viewContainerRef.createEmbeddedView(this.templateRef);
        //Eğer şart doğruysa ilgili template ViewContainerRef nesnesi ile görüntünün içerisine yerleştirilerek ilgili element gösterilir.
        else
          this.viewContainerRef.clear();
        //Yok eğer şart yanlışsa ilgili template ViewContainerRef nesnesi ile görüntünün içerisinden temizlenerek ilgili element temizlenir.
      }
    
    }
    

    Yukarıdaki kod bloğunu incelerseniz eğer “@Input” deklarasyonu ardından “set” keywordü ile direktifle aynı isimde boolean bir parametre alan “CustomIf” metodu oluşturulmuş bulunmaktadır. Parametredeki değerin “true” ya da “false” durumuna göre ilgili direktifin kullanıldığı eleman gösterilecek ya da gösterilmeyecektir.

    Kullanımı;

    <div *CustomIf="show">
      <p>Hello World!</p>
    </div>
    
  • Döngü Yapısı İçin Özel Yapısal Direktif Oluşturma

    import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
    
    @Directive({
      selector: '[CustomLoop]'
    })
    export class CustomLoopDirective {
      constructor
        (
        private templateRef: TemplateRef<any>,
        private viewContainerRef: ViewContainerRef
        ) { }
      @Input() set CustomLoop(num: number) {
        for (let i = 0; i < num; i++)
          this.viewContainerRef.createEmbeddedView(this.templateRef);
      }
    }
    

    Kullanımı;

    <div *CustomLoop="5">
      <p>Hello World!</p>
    </div>
    

    Sonuç olarak ekrana beş adet “Hello World!” değerini yazacaktır.

  • forEach Yapısı İçin Özel Yapısal Direktif Oluşturma

    import { Directive, TemplateRef, ViewContainerRef, Input } from '@angular/core';
    
    @Directive({
      selector: '[CustomEach]'
    })
    export class CustomEachDirective {
      constructor
        (
        private templateRef: TemplateRef<any>,
        private viewContainerRef: ViewContainerRef
        ) { }
    
      @Input() set CustomEach(source: Array<any>) {
        source.forEach((element: string) => {
          this.viewContainerRef.createEmbeddedView(this.templateRef);
        });
      }
    }
    

    Kullanımı;

    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"];
    }
    
    <div *CustomEach="source">
      <p>Hello World!</p>
    </div>
    

    Kaynakta üç adet veri olduğundan dolayı “Hello World!” değerini üç kere tekrarlayan çıktı verecektir.

Ş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"];
}

Angular 5 - Custom Structural Directives

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>

Angular 5 - Custom Structural Directives

İç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…

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir