Angular 9 Pipe Nedir? Nasıl Çalışır?

Mehmed Emre AKDİN
SDTR

--

Yeniden Herkese Merhaba.

Umarım iyisinizdir ve her şey yolunda gidiyordur. Bugün Angular’da Pipe kavramını ele almaya çalışacağım. Çok kullanışlı yapılar olan pipe’ların bize ne gibi kolaylıklar sağladığına hep beraber yakından bakalım diyorum. Eğer kod editörleriniz hazırsa yavaştan başlayalım.

Pipe Nedir?

“Angular Pipe” ‘ı kısaca ve basitce ifade etmek gerekirse, filtreleme veya dönüştürme olarak tanımlayabiliriz. Elinizdeki bir değeri veya veriyi pipe’lara göndeririz, ardından bu değer işlenir ve kendi içindeki dönüşümlere göre yeni bir değer üretilir. Veya değeri pipe’a verirken yeni üretilecek bu değer için pipe’a tıpkı fonksiyonlarda olduğu gibi bazı argümanlar verebiliriz. Aslında pipe’ları fonksiyonlara benzetebiliriz. Çünkü çalışma mantıkları fonksiyonlar ile benzerdir. Pipe’ların ne kadar kullanışlı bir yapı olduğunu görünce daha çok seveceğinize eminim.

Neden Pipe Kullanmalıyız?

Evet hemen hemen hepinizin aklına neden pipe kullanalım? Verileri zaten .html dosyasına ait .ts dosyasında dönüştürebiliyoruz gibi sorular gelmiştir.

Şimdi aşağıdaki örneği inceleyerek bu sorunun cevabını veremeye çalışalım:

Bir string ifadedeki harflerin hepsini büyük harfe çevirmeye çalışalım. Bunu hem pipe hem de geleneksel yöntemle yapalım:

Geleneksel Yaklaşım :

App.component.ts

value:string = "Mehmed Emre Akdin"formattedValue:string;ngOnInit(){this.formattedValue = this.value.toUpperCase();}

App.component.html

<p>{{formattedValue}}</p>

Pipe Yaklaşımı:

app.component.ts

value:string = "Mehmed Emre Akdin"

app.component.html

<p>{{value|uppercase}}</p>

Evet örneğe bakarak şunları söyleyebiliriz:

  • Geleneksel yaklaşımda “value” değerini değiştirmeden .html tarafında gösteremeyiz. Yani “value” değişkenine atanan metinsel ifadeyi büyük harflerle göstermek istediğimizde “value” ‘ nun değerini değiştirmemiz gerekir. Bu değişkeni başka iş katmanlarında kullanabileceğimizi düşünürsek sıf bunu değişmemek için örnekte olduğu gibi yeni bir değişken tanımlamalıyız. Fakat pipe kullandığımızda buna gerek kalmayacaktır. (Not: “value” değerini değiştirmeden .ts tarafında fonksiyon kullanarak bu işi yapabiliriz fakat bu fonksiyonu farklı bir .html dosyasında kullanmak istediğimizde bu .html dosyasına ait .ts dosyasında yine bu fonksiyonu tanımlamak zorundayız. Ve basit veya kompleks her işlem için bir fonksiyon tanımlamak ekstra maliyet demektir.)
  • Geleneksel yöntemde .html dosyasına bakarak verinin ekranda nasıl görüntüleneceğini bilemeyiz. Bunun için .ts dosyasına bakmak zorunda olacağız. Fakat pipe kullanırsak {{value|uppercase}} ifadesine bakarak metinsel ifadenin ekrana büyük harflerle yazılacağını hemen anlayacağız.

Built-In Pipes

Bu bölümde Angular’ın bizim hizmetimize sunduğu hazır pipe’lardan bahsetmek istiyorum. Birkaç tane hazır pipe gördükten sonra asıl işimiz olan özelleştirilmiş pipe’lara geçeceğiz.

Pipe’ı kullanabilmek için gerekli yapı aşağıdaki şekildedir:

{{ variable | pipe-name:arg}}

variable: Bu kısımda filtrelenecek değer belirtilir. Genel .html’in .ts dosyasındaki bir değişkendir.

pipe-name: Bu kısımda kullanılacak pipe’ın adı belirtilir.

arg: Bu kısım opsiyoneldir. Ve pipe’a verilecek olan parametreyi belirtir.

1-) Lowercase, Uppercase ve Titlecase Pipe

Lowecase pipe kendisine değer olarak verilen string ifadesindeki tüm harfleri küçük harf yapar. Uppercase tam tersini yani kendisine değer olarak verilen string ifadesindeki tüm harfleri büyük harf yapar. Titlecase ise kendisine değer olarak verilen string ifadesinde boşluktan sonra gelen her kelimenin ilk harfini büyük yapar. Ardından kalan kısımı ise küçük yapar.

{{ value_expression | titlecase }}

Input Value:

value → string

app.component.ts’in içeriği:

export class AppComponent {name:string = "MehmeD emre akdiN";}

app.component.html’in içeriği:

<p>Merhaba {{name}}</p><p>Merhaba {{name|lowercase}}</p><p>Merhaba {{name|uppercase}}</p><p>Merhaba {{name|titlecase}}</p>

Sonuç:

MehmeD emre akdiNMerhaba mehmed emre akdinMerhaba MEHMED EMRE AKDINMerhaba Mehmed Emre Akdin

2-) Date Pipe

Bu pipe tarih üzerinde belirli formatlama işlemleri yapmamızı sağlar.

{{ value_expression | date [ : format [ : timezone [ : locale ] ] ] }}

Parameters:

format : Tarihin formatını belirler. Örneğin: ‘MM/dd/yyyy’ , ‘short’ veya ‘medium’ gibi.

timezone : Belirli bir saat diliminde tarihi görüntülemek için kullanılır. Örneğin : ‘IST’ gibi.

locale : Tarihi ülkenin biçim kurallarına göre görüntülemek için kullanılır. Örneğin : ‘fr’ gibi.

Input Value:

value → string|number|Date

app.component.ts’in içeriği:

export class AppComponent {today = new Date().toDateString();}

app.component.html’in içeriği:

<p>{{today}}</p><p>{{today|date}}</p><p>{{today|date:"dd/MM/yyyy"}}</p><p>{{today|date:"yyyy/dd/MM"}}</p><p>{{today|date:'medium':'IST':'fr'}}</p>

Önemli Not: ‘fr’ yani fransa yerel ayarlarını kullanmanız için aşağıdaki adımları .html’in .ts dosyasında uygulamanız gerekmekte:

import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeFr, 'fr'); //ngOInit içinde

Sonuç:

Fri Jan 22 2021Jan 22, 202122/01/20212021/22/01

Görüldüğü üzere farklı tarih formatları için pipe’ın argüman kısmına belirli değerler vermemiz yeterlidir.

3-) Currency Pipe

Para birimleri üzerinde belirli formatlama işlemleri yapmamızı sağlar.

{{ value_expression | currency [ : currencyCode [ : display [ : digitsInfo [ : locale ] ] ] ] }}

Parameters:

currencyCode : Para biriminin türünü temsil eder. Örneğin : “EUR” euro’nun simgesini ifade eder.

display :Varsayılan para birimi simgesinin yerine kendi koyduğunuz herhangi bir değerin görüntülenmesini istiyorsanız kullanılır. ‘symbol’ ve ‘symbol-narrow’ olarak iki tane özel değer alabilir. Genelde iki para biriminin olduğu ülkelerde kullanılır. Örneğin Kanada.

digitsInfo : Sayının ondalık noktalarını özelleştirmek istediğimizde kullanılır. Söz dizimi aşağıdaki şekildedir:

“numberOfInteger: minimumFractions-maxFractions”

  • numberOfInteger : Ondalıktan(noktadan) önce gösterilmek istenen basamak sayısı
  • minimumFractions : Ondalıktan(noktadan) sonra gösterilmek istenen minumum basamak sayısı
  • maxFractions : Ondalıktan(noktadan) sonra gösterilmek istenen maksimum basamak sayısı

locale : Yerel para biriminin pipe’a geçirilmesini sağlar.

Input Value:

value → string|number

app.component.ts’in içeriği:

import { Component } from '@angular/core';
import { registerLocaleData } from '@angular/common'; // 'fr' local ayarlar için
import localeFr from '@angular/common/locales/fr'; // 'fr' local ayarlar için
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {cash:number = 500.67; ngOnInit(): void { registerLocaleData(localeFr, 'fr'); // 'fr' local ayarlar için }}

app.component.html’in içeriği:

<p>{{cash|currency:'EUR'}}</p><!--Kanadanin iki tane para birimi vardır--><p>{{cash|currency:'CAD':'symbol'}}</p><p>{{cash |currency:'CAD':'symbol-narrow'}}</p><p>{{cash|currency:'EUR':'':'5.2-2'}}</p><p>{{cash|currency:'EUR':'':'5.2-2':'fr'}}</p>

Sonuç:

€500.67CA$500.67$500.6700,500.67

4-) Json Pipe

Verilen değeri Json temsiline dönüştürür.

{{ value_expression | json }}

Input Value:

value → any

app.component.ts’in içeriği:

export class AppComponent { data:object={  name:"Mehmed",  secondName:"Emre",  lastName:"Akdin" }
}

app.component.html’in içeriği:

<p>{{data|json}}</p>

Sonuç:

{ "name": "Mehmed", "secondName": "Emre", "lastName": "Akdin" }

5-) Percent Pipe

Bir değeri belirli kurallara göre yüzde olarak biçimlendirir.

{{ value_expression | percent [ : digitsInfo [ : locale ] ] }}

Parameters:

digitsInfo : Para birimindeki “digitsInfo” ile aynı işlevi görür.

locale : Kullanılacak yerel biçim kuralları için bir yerel ayar kodudur.

Input Value:

value → string | number

app.component.ts’in içeriği:

import { Component } from '@angular/core';
import { registerLocaleData } from '@angular/common'; // 'fr' local ayarlar için
import localeFr from '@angular/common/locales/fr'; // 'fr' local ayarlar için
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent { value1:number = 0.75; value2:string = "25"; ngOnInit(): void { registerLocaleData(localeFr, 'fr'); // 'fr' local ayarlar için }}

app.component.html’in içeriği:

<p>{{value1|percent:'7.3-3'}}</p><p>{{value2|percent:'7.3-3':'fr'}}</p>

Sonuç:

0,000,075.000%0 002 500,000 %

6-) Slice Pipe

Değerin yeni bir alt kümesini içeren bir string veya array oluşturur.

{{ value_expression | slice : start [ : end ] }}

Parameters:

start : Alt kümenin başlangıç indexi.

end : Alt kümenin bitiş indexi.

Input Value:

value → string | readonly T[]

app.component.ts’in içeriği:

export class AppComponent {myString:string="Mehmed Emre AKDİN"}

app.component.html’in içeriği:

Sonuçları direk görmeniz açısından yanlarına yorum satırı olarak yazdım.

<!--Mehmed Emre AKDİN--><!--"M" harfinin öncesi 0. indextir.--><!--"M" harfinin sonrası 1. indextir.--><p>{{myString|slice:0:3}}</p> <!--Meh--><p>{{myString|slice:3:6}}</p> <!--med--><!--Sadece başlangıç indexi girildi.--><!--Bu yüzden 6. indexten sonraki tüm değerler alındı--><p>{{myString|slice:6}}</p> <!--Emre AKDİN--><!--Tersten -6.index aynı normalda 11. indextir.--><p>{{myString|slice:-6}}</p> <!--AKDİN--><p>{{myString|slice:11}}</p> <!--AKDİN--><p>{{myString|slice:7}}</p> <!-- Emre AKDİN--><!--Başlangıç indexi 0, bitiş indexi -6.--><!--Bu yüzden -6. indexe kadar olan kısmı alacak.--><!--(-6. index) tersden gidildiğinde "AKDİN" kelimesininin başına denk geliyor.--><!--Yani normalde 11.index.--><p>{{myString|slice:0:-6}}</p> <!--Mehmed Emre--><!-- Emre'nin sonundaki "e" harfinin öncesi  hem 10. indextir. Hemde -7. indextir  --><!-- Emre'nin sonundaki "e" harfinin sonrası hem 11. indextir. Hemde -6. indextir  --><p>{{myString|slice:-7:-6}}</p> <!--e--><p>{{myString|slice:10:11}}</p> <!--e-->

Bu verdiğim built-in pipe’ların haricinde de farklı built-in pipe’lar mevcut. Ben en çok karşımıza çıkanları göstermek istedim. Eğer izin verirseniz “custom-pipe” konusuna giriş yapmak istiyorum.

Custom Pipes

Evet adından da anlaşılacağı üzere hazır olarak kullandığımız pipe’lar dışında kendi isteğimize göre pipe’lar oluşturabiliyoruz. Aslında işin eğlenceli kısmı tam olarak burada başlıyor. Kendi pipe’larımızı oluşturup nasıl çalıştıklarını daha derinden öğrenmenin işin mantığını kavramada ve pekiştirmede bizi bir üst seviyeye taşıyacağını umuyorum. Bundan ötürü bu konudan sonra kodunuzda pipe’lara daha çok yer vereceğinizi düşünüyorum.

Hep birlikte src klasörünün altında bir libs klasörü oluşturalım. Ardından içine pipe’larımızı yerleştireceğimiz bir pipes isimli klasör oluşturalım. Bunu manuel olarak yapabilirsiniz. CLI komutuna ise aşağıdaki şekilde yazmanız yeterlidir:

cd src                  //cd ile belirtilen dizine gidilir
mkdir libs //mkdir klasör oluşturur
cd libs
mkdir pipes
cd pipes

Ardından “upper” isimli bir pipe oluşturmak istediğimizi varsayalım. Aşağıdaki sihirli komutu girerek pipe’mızı oluşturuyoruz :D

ng generate pipe upper

Not: Pipe, component ve modül gibi bileşenleri oluştururken kısaltmalar kullanabiliriz:

- ng g p upper

Evet aşağıdaki hata ile karşılaştıysanız doğru yoldasınız demektir:

1.0) Hata

Angular oluşturduğumuz pipe, component ve modül gibi bileşenleri en yakın modülde bizim için tanımlar. Bu yüzden pipe’ı modülümüzün bulunduğu klasörde oluştursaydık, angular bizim için modülümüzde pipe’mızı declare edecekti. Fakat biz libs/pipes altında işlem yapıyoruz ve burada herhangi bir modül yok. Bu yüzden angular bu otomatik tanımlamayı atlamak istiyorsanız otomatik içe aktarmayı atla seçeneğini kullanın diyor.

Bu yüzden CLI komut satırına aşağıdaki komutu giriyoruz:

ng g p upper --skip-import

— skipt-import komutu otomatik içe aktarmayı iptal edecektir.

libs/pipes altında aşağıdaki iki dosyanın oluştuğunu görüyoruz:

1.1) Dosya Görüntüsü

Önemli Not :

Oluşturduğumuz pipe sınıfımızı o pipe’ı kullandığımız componentin bağlı olduğu modüle declare etmeyi unutmayın!

Spec dosyaları, kaynak dosyalarımız için unit testlerdir. Şimdilik .spec uzantılı dosya ile ilgilenmiyeceğiz. Bizim işimiz “upper.pipe.ts” dosyasında. Bu dosya içeriği aşağıdaki gibidir:

1.2) upper.pipe.ts

Evet aslında “custom pipe” dediğimiz şeyin bir sınıfın olduğunu görüyoruz. Fakat bir şartla. Bu sınıfın “PipeTransform” isimli interface’i implemente etmesi ve bu interface içindeki “transform” isimli fonksiyonu override etmesi gerekiyor. Pipe ile ilgili işlemlerimizi “transform” fonksiyonu içinde gerçekleştireceğiz. Fonksiyonun parametrelerinin ne işe yaradığını resim üzerinde açıkladım. Umarım anlaşılmıştır. Örnekler ile olayın daha iyi kavranacağını düşünüyorum.

Not :

“transform” fonksiyonumuzun ikinci parametresinin bir rest parametresi olduğuna dikkat edin. Aynı tipte alacağımız parametre sayısı belli olmadığında rest parametresi kullanılır.

1-) Custom Pipe Simple Example

Çok basit bir örnekle “custom pipe” oluşturmaya başlayalım. Length isimli bir pipe’mız olsun ve bu pipe verilen string ifadenin sonuna o stringin uzunluğu “-lenght” şeklinde eklesin. Örneğin sonuç çıktısı şu şekilde olsun :

mehmed emre -11

Haydi kodlamaya geçelim:

app.component.ts’in içeriği:

value:string="Mehmed Emre AKDİN"

app.component.html’in içeriği:

<p>{{value|length}}</p>

length.pipe.ts ’ in içeriği:

import { Pipe, PipeTransform } from '@angular/core';@Pipe({
name: 'length'
})
export class LengthPipe implements PipeTransform {transform(value: string, ...args: unknown[]): unknown { return `${value} - ${value.length}`;
//string interpolation kullanıldı
}
}

Sonuç:

Mehmed Emre AKDİN - 17

Çok basit bir örnekle ilk pipe’mızı yazmış bulunuyoruz. “transform” fonksiyonumuzun sadece ilk parametresini kullandık. Ve bu ilk parametreye yani “value” parametresine “Mehmed Emre AKDİN” değeri geldi ve biz bu değerin sonuna string interpolation kullanarak isimin uzunluğu ekledik.

Şimdi yavaş ve emin adımlarla “transform” fonksiyonumuzun ikinci parametresinide kullanalım. Bildiğiniz üzere ikinci parametremiz pipe’a verilen argümanlardı. Bu argümanı şöyle kullanalım. Eğer string ifademizin uzunluğu, girdiğimiz argüman değerinden büyükse hesaplama yapılmasın. Yani “Mehmed Emre AKDİN” değeri için “10” argümanını girersek “10 < 17” olduğundan herhangi bir uzunluk değeri metin yanına yazılmasın. Bu şekilde hesaplama yapılacak üst değeri belirtmiş olalım.

app.component.html’in içeriği:

<p>{{value|length:10}}</p><p>{{value|length:19}}</p>

length.pipe.ts ’ in içeriği:

import { Pipe, PipeTransform } from '@angular/core';@Pipe({
name: 'length'
})
export class LengthPipe implements PipeTransform { transform(value: string, ...args: number[]): unknown { return args[0] >= value.length ? `${value} - ${value.length}` : `${value}` ; }
}

Sonuç:

Mehmed Emre AKDİNMehmed Emre AKDİN - 17

Yine çok basit bir şekilde ikinci parametremizi de işin içine katmış olduk. Bu iki örnek mantıksal açıdan pek işe yaramaz görünse bile olayı bize güzel özetleyeceğini ve açıklayacağını düşündüm. Şimdi biraz daha farklı örnekler ele almak istiyorum.

2-) Custom Pipe Filter Example

Şimdi pipe’ın klasik örneklerinden biri olan filtreleme işlemini yapmayı deneyelim. Bir tane arama inputumuz olsun ve isime göre listede arama yapalım.

Not :

Bu örneğimiz için bootstrap ve jquery kütüphanesini projemize ekleyeceğiz.

app.component.ts’in içeriği:

import { Component } from '@angular/core';
import { Person } from 'src/model/Person';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {text:string = ""persons:Person[]=[ {
name:"Mehmed Emre",
surname:"AKDİN"
},
{
name:"Sadık",
surname:"Aydın"
},
{
name:"Ceren",
surname:"Ulus"
},
{
name:"Sevgi",
surname:"Türk"
},
{
name:"Ceren",
surname:"Katip"
}
] }

app.component.html’in içeriği:

<div class="container"><div class="row"><div class="col-sm-12 mb-2"><form class="form-inline my-2 my-lg-0"><input class="form-control mr-sm-2"  placeholder="isime göre ara" name="name" [(ngModel)] = "text"></form></div><div class="col-sm-8"> <ul class="list-group">   <li class="list-group-item mb-3" *ngFor ="let person of  persons|filter:text" >   <p>{{person.name}}</p>   <p>{{person.surname}}</p>  </li> </ul></div></div>
</div>

filter.pipe.ts ’ in içeriği:

import { Person } from 'src/model/person';import { Pipe, PipeTransform } from '@angular/core';@Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform { transform(value: Person[], text:string): Person[] { if(!text){
return value;
}
return value.filter(p => p.name.toLowerCase().indexOf(text.toLowerCase())!=-1); }
}
}

Eğer “text” boş gelirse yine “text” değerini döndüreceğiz. Aksi takdirde gelen dizi yani “value” üzerinde “filter” fonksiyonu ile dolaşacağız. Eğer ki girmiş olduğumuz parametre yani “text” değerimiz “value” dizisinin name ifadesi içinde geçiyorsa “value” dizisinin bu elemanını geri döndüreceğiz. Ve bunu tüm dizi elemnları için her defasında yapacağız.

app.module.ts ’ in içeriği:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';
import { FilterPipe } from 'src/libs/pipes/filter.pipe';
@NgModule({declarations: [
AppComponent,
FilterPipe
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Not :

Yukarıda görüldüğü üzere app.module.ts’e “FilterPipe” ‘mızı declare ediyoruz. Ek olarak arama inputumuza girdiğimiz string ifadenin direk pipe’a parametre olarak gitmesini sağlamak için .html tarafında [(ngModel)] kullanmamız gerekiyor.(bknz:”two way data binding”) [(ngModel)]’i kullanabilmek için “FormsModule” ’ü import ediyoruz.

Sonuç:

Görüldüğü üzere filtreleme işlemimiz başarılı bir şekilde gerçekleşmiştir.

3-) Character-count Pipe

Parametre olarak girdiğimiz herhangi bir harfin verilen metinde kaç kere geçtiğini bulmak isteyelim. Örneğin:

{{text:|character-count|’a’}} //Sonuç →2

{{text:|character-count|’b’}}//Sonuç →7 gibi.

app.component.ts’in içeriği:

export class AppComponent {
text:string = "Merhaba ben Mehmed Emre AKDİN"
}

app.component.html’in içeriği:

<div class="container">
<div class="row">
<div class="col-sm-8">
<ul class="list-group">
<p>{{text|characterCount:'i'}}</p>
</ul>
</div>
</div>
</div>

length.pipe.ts ’ in içeriği:

import { Pipe, PipeTransform } from '@angular/core';@Pipe({
name: 'characterCount'
})
export class CharacterCountPipe implements PipeTransform {transform(value: string, ...character: string[]): number { let empty = "";
let count = 0;
value.split("").forEach(val=>{ if(val.toLocaleLowerCase() == character[0].toLocaleLowerCase())
count++;
})
return count;
}
}

Sonuç:

3

Örnekte ‘a’ harfinin verilen cümlede kaç kere geçtiğini basitçe kontrol etmiş olduk.

Örneklerin çeşiti daha da arttırılabilir. Artık bu sizin hayal gücünüze kalmış. Mutlaka basitte olsa özel bir pipe yazmanızı tavsiye ediyorum İşin mantığını anlama açısından çok faydalı olacağına eminim.

Yazımı daha da uzatmamak adına burada sonlandırıyorum. Umarım anlaşılır bir içerik olmuştur. Eğer gözümden kaçan yazım hatası veya anlatım bozukluğu olduysa şimdiden kusuruma bakmayın. Okuduğunuz için teşekkürler. Bir sonraki yazıda görüşmek üzere!

Bu konuyla alakalı ulaşabileceğiniz bazı güvenilir kaynaklar:

https://angular.io/

https://www.angularjswiki.com/

--

--