Giriş ve Proje
Hazırlığı
Angular dünyasına
adım atarken ilk yapmanız gereken şey, bilgisayarınızı bu modern framework ile çalışacak hale getirmektir. Bu süreçte arka
planda çalışan araçları, projelerin nasıl ayağa kaldırıldığını ve temel HTML
iskeletinin Angular projelerinde nasıl şekillendiğini
bilmeniz gerekir.
Angular Kurulum
Dosyaları ve Ortam Hazırlığı
Angular ile kod
geliştirmek için bilgisayarınızda Node.js tabanlı bir ekosistemin kurulu
olması şarttır. Node.js, JavaScript kodlarını tarayıcı dışında çalıştırmaya
yarayan bir runtime (çalışma zamanı) ortamıdır.
Node.js yüklediğinizde, beraberinde npm (Node Package Manager) adı
verilen bir paket yöneticisi de gelir.
Angular'ın tüm
nimetlerinden yararlanmak ve projeleri hızlıca oluşturmak için Angular CLI (Command Line Interface - Komut Satırı Arayüzü) aracını bilgisayarımıza küresel
olarak yükleriz.
Uygulama - Angular CLI Kurulumu:
Terminalinizi veya komut satırınızı açarak aşağıdaki komutu yazın:
bash
npm install -g @angular/cli
Kodu dikkatli kullanın.
Bu komuttaki -g parametresi, Angular CLI'ın bilgisayarınızın
her yerinden erişilebilir olmasını sağlar. Kurulum tamamlandıktan sonra her
şeyin yolunda olduğunu doğrulamak için şu komutla versiyon kontrolü
yapabilirsiniz:
bash
ng version
Kodu dikkatli kullanın.
Proje Açılışı ve Projenin Çalıştırılması
Ortam hazır olduktan sonra, Angular
CLI bize tek bir komutla tüm klasör yapısı, yapılandırma dosyaları ve test
ortamı hazır olan devasa bir şablon sunar. Bir proje oluştururken Angular bize yönlendirmeli sorular sorar. Bu sorular
arasında Routing (Yönlendirme) kullanıp kullanmayacağımız ve hangi stil
şablonunu (CSS, SCSS, SASS vb.) tercih edeceğimiz yer alır. Modern
projelerde esneklik adına SCSS seçimi oldukça yaygındır.
Uygulama - Yeni Proje
Oluşturma ve Çalıştırma:
Yeni bir şifre üretici uygulaması yapmak için terminale şu komutu yazarız:
bash
ng new sifre-uretici
Kodu dikkatli kullanın.
Komut çalıştıktan sonra size sorulan "Would you like
to add Angular
routing?" sorusuna Yes (Y), stil
seçimine ise SCSS diyerek ilerleyebilirsiniz. Proje klasörü
oluştuktan sonra klasörün içine girip yerel geliştirme sunucusunu başlatırız:
bash
cd sifre-uretici
ng serve --open
Kodu dikkatli kullanın.
--open (veya kısaca -o) parametresi,
projenin derlenmesi bittiğinde varsayılan internet tarayıcınızda otomatik
olarak http://localhost:4200 adresini
açar.
HTML Kemik Yapı
Angular projelerinde
her bileşenin (component) kendine ait bir HTML
şablonu bulunur. Şifre oluşturucu projemizin temel arayüzünü oluşturmak için
ana bileşenimiz olan app.component.html dosyasının içini temizleyip
temel form elemanlarını yerleştiririz. Bu yapıda kullanıcıdan girdiler almak
için girdi alanları (input), etiketler (label) ve onay kutuları (checkbox) kullanırız.
Uygulama - Şifre
Oluşturucu HTML İskeleti:
src/app/app.component.html dosyasını
açın ve şu kodları ekleyin:
html
<div class="container">
<h1>Şifre
Oluşturucu</h1>
<div class="form-group">
<label
for="uzunluk">Şifre Uzunluğu:</label>
<input
type="number" id="uzunluk"
class="input-field"
/>
</div>
<div class="form-group">
<input
type="checkbox"
id="harf" />
<label
for="harf">Harfleri Kullan (A-Z,
a-z)</label>
</div>
<div class="form-group">
<input
type="checkbox"
id="sayi" />
<label
for="sayi">Sayıları
Kullan (0-9)</label>
</div>
<div class="form-group">
<input
type="checkbox"
id="sembol" />
<label
for="sembol">Sembolleri Kullan
(!@#$)</label>
</div>
<button
class="btn-generate">Şifre
Üret</button>
<div class="result-group">
<label>Üretilen
Şifre:</label>
<input
type="text" readonly class="result-input" />
</div>
</div>
Kodu dikkatli kullanın.
Bu aşamada sadece görsel elemanlar ekrandadır;
henüz hiçbir buton veya kutu işlevsel değildir. Bunları işlevsel hale getirmek
için Angular'ın veri bağlama yöntemlerini öğrenmemiz
gerekir.
Temel Angular Kavramları ve Veri Bağlama (Data Binding)
Angular'ın en güçlü
yanı, TypeScript (mantık katmanı) ile HTML (görünüm
katmanı) arasındaki veri iletişimini çok kolay hale getirmesidir. Bu iletişime Data
Binding (Veri Bağlama) denir.
Event Binding Genel Bakış
Kullanıcının ekranda yaptığı hareketleri
(tıklama, klavyeden yazma, farenin üzerine gelmesi) TypeScript
tarafında yakalamak için Event Binding kullanılır. HTML elementindeki olay parantez
içinde (olayIsmi) şeklinde
yazılır ve karşısına TypeScript'teki fonksiyon
bağlanır.
Örnek:
html
<button (click)="onSifreUret()">Şifre Üret</button>
Kodu dikkatli kullanın.
Property Binding ve Interpolation
Örnek:
html
<!-- Interpolation Örneği
-->
<p>Seçilen Uzunluk: {{ uzunluk Degeri
}}</p>
<!-- Property Binding Örneği -->
<button [disabled]="!harfSecildi && !sayiSecildi">Gönder</button>
Kodu dikkatli kullanın.
Gelişmiş Uygulama: Şifre Oluşturucu Mantığı ve
Arayüzü
Şimdi tüm bu öğrendiklerimizi birleştirerek
çalışan, dinamik bir şifre oluşturucu uygulaması yapalım. Kullanıcının
girdilerini anlık olarak yakalayacak, doğrulayacak ve rastgele karakterler
seçerek şifre üreteceğiz.
Adım 1: TypeScript
Kodlarının Yazılması (src/app/app.component.ts)
typescript
import { Component }
from '@angular/core';
@Component({
selector:
'app-root',
templateUrl:
'./app.component.html',
styleUrls:
['./app.component.scss']
})
export class AppComponent {
uzunluk: number = 0;
harfKullan:
boolean = false;
sayiKullan:
boolean = false;
sembolKullan:
boolean = false;
uretilenSifre:
string = '';
// Kullanıcı şifre uzunluğunu değiştirdiğinde
tetiklenir
onUzunlukDegis(olay: Event) {
const
hedef = olay.target as HTMLInputElement;
const
girilenDeger = parseInt(hedef.value);
// Güvenli kullanım kontrolü (Sayı değilse
veya boşsa 0 yap)
if
(!isNaN(girilenDeger)) {
this.uzunluk
= girilenDeger;
} else {
this.uzunluk
= 0;
}
}
// Onay kutularının durumunu yakalayan
fonksiyonlar
onHarfDegis() {
this.harfKullan
= !this.harfKullan;
}
onSayiDegis() {
this.sayiKullan
= !this.sayiKullan;
}
onSembolDegis() {
this.sembolKullan
= !this.sembolKullan;
}
// Rastgele şifre üreten ana fonksiyon
onSifreUret() {
const
harfler = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const
sayilar = '0123456789';
const
semboller = '!@#$%^&*()_+~`|}{[]:;?><,./-=\\';
let
gecerliKarakterler = '';
if
(this.harfKullan) gecerliKarakterler
+= harfler;
if
(this.sayiKullan) gecerliKarakterler
+= sayilar;
if
(this.sembolKullan) gecerliKarakterler
+= semboller;
let
olusturulanSifre = '';
for
(let i = 0; i < this.uzunluk;
i++) {
const
rastgeleIndeks = Math.floor(Math.random() * gecerliKarakterler.length);
olusturulanSifre
+= gecerliKarakterler[rastgeleIndeks];
}
this.uretilenSifre
= olusturulanSifre;
}
}
Kodu dikkatli kullanın.
Adım 2: HTML Şablonunun Bağlanması (src/app/app.component.html)
html
<div class="sifre-kutusu">
<h2>Güvenli
Şifre Tasarımcısı</h2>
<div class="alan">
<label>Uzunluk
Girin:</label>
<!-- (change) yerine anlık yakalamak için (input)
olayı da kullanılabilir -->
<input
type="number" (input)="onUzunlukDegis($event)" />
</div>
<div class="alan-checkbox">
<input
type="checkbox"
id="chk-harf" (change)="onHarfDegis()" />
<label
for="chk-harf">Harf
İçersin</label>
</div>
<div class="alan-checkbox">
<input
type="checkbox"
id="chk-sayi" (change)="onSayiDegis()" />
<label
for="chk-sayi">Sayı
İçersin</label>
</div>
<div class="alan-checkbox">
<input
type="checkbox"
id="chk-sembol" (change)="onSembolDegis()" />
<label
for="chk-sembol">Sembol
İçersin</label>
</div>
<!-- Property Binding: Eğer uzunluk 0
ise veya hiçbir kutu seçilmediyse butonu devre dışı bırak -->
<button
class="btn-ana"
[disabled]="uzunluk
=== 0 || (!harfKullan && !sayiKullan
&& !sembolKullan)"
(click)="onSifreUret()">
Şifre Oluştur
</button>
<!-- NgIf Kullanımı: Eğer şifre üretildiyse bu alanı göster
-->
<div class="sonuc-alani" *ngIf="uretilenSifre !==
''">
<label>Şifreniz:</label>
<input
type="text" readonly [value]="uretilenSifre" />
</div>
</div>
Kodu dikkatli kullanın.
Burada kullanılan *ngIf bir yapısal direktiftir (structural
directive). Eğer içerisindeki şart doğru (true) ise o HTML bloğunu sayfaya ekler, yanlış (false) ise HTML bloğunu DOM ağacından tamamen
kaldırır. Başlangıçta uretilenSifre boş bir metin
('') olduğu için şart sağlanmaz ve sonuç kutusu ekranda gizlenir. Butona
basılıp şifre üretildiğinde kutu görünür hale gelir.
Bileşenler
(Components) ve Yapısal Direktifler (NgFor, NgIf)
Angular projeleri
lego parçaları gibi bir araya gelen küçük bileşenlerden oluşur. Her bir lego
parçasına Component (Bileşen) denir. Projeyi tek bir HTML dosyasında
yazmak yerine parçalara ayırarak yönetilebilirliği artırırız.
Component Mantığı ve Anatomisi
Bir bileşen temel olarak üç dosyadan oluşur:
Her bileşenin bir @Component dekoratörü ve
bu dekoratör içinde tanımlanmış bir selector
(seçici) ifadesi vardır. Örneğin selector ifadesi
app-kart olan bir
bileşeni, ana HTML dosyasında <app-kart></app-kart> şeklinde bir HTML etiketiymiş gibi çağırıp
kullanabiliriz.
NgFor Kullanımı
Bir veri dizisindeki (array)
elemanları ekranda alt alta veya yan yana listelemek için *ngFor direktifini kullanırız. Bu yapı bir döngü gibi
çalışarak dizide kaç eleman varsa o kadar HTML elementini dinamik olarak
çoğaltır.
Kullanım Yapısı:
html
<div *ngFor="let tekilEleman of elemanlarDizisi">
{{ tekilEleman
}}
</div>
Kodu dikkatli kullanın.
Uygulama: Dinamik Kart Projesi (Dizi Verilerini
Ekrana Basma)
Bir sosyal medya veya blog uygulamasındaki gibi
kartları listeleyeceğimiz bir senaryo hazırlayalım. Projeye harici bir
kütüphane eklemeyi (örneğin popüler CSS kütüphanesi Bulma CSS veya Bootstrap) ve bu kütüphane tarzında kartları
listelemeyi görelim.
Adım 1: Projeye Stil Kütüphanesi Ekleme
Terminalden projeye stil kütüphanesini indirin:
bash
npm install bulma
Kodu dikkatli kullanın.
Ardından projenin küresel stil dosyası olan src/styles.scss dosyasını
açıp bu kütüphaneyi içeri aktarın:
scss
@import 'bulma/css/bulma.css';
Kodu dikkatli kullanın.
Adım 2: Alt Bileşenin (Card
Component) Oluşturulması
Angular CLI yardımıyla otomatik olarak yeni bir kart
bileşeni açalım:
bash
ng g c card
Kodu dikkatli kullanın.
(Not: g generate, c ise component kelimesinin kısaltmasıdır).
Adım 3: Parent (Ana) Component'te Verilerin Hazırlanması (src/app/app.component.ts)
Ana bileşenimizde kartların içinde sergilenecek nesne dizisini (object array) hazırlarız.
typescript
import { Component }
from '@angular/core';
@Component({
selector:
'app-root',
templateUrl:
'./app.component.html',
styleUrls:
['./app.component.scss']
})
export class AppComponent {
blogYazilari
= [
{
baslik:
'Doğa Yürüyüşü',
kullaniciAdi:
'@gezgin',
icerik:
'Bugün harika dağ havasında 10 kilometre yürüdük.',
resimUrl:
'assets/doga.jpg'
},
{
baslik:
'Yazılım Öğrenmek',
kullaniciAdi:
'@kodcu',
icerik:
'Angular ile component
yapısını öğrenmek çok keyifli.',
resimUrl:
'assets/kod.jpg'
},
{
baslik:
'Lezzetli Tarifler',
kullaniciAdi:
'@gurme',
icerik:
'Ev yapımı nefis İtalyan pizzası tarifimi paylaşıyorum.',
resimUrl:
'assets/pizza.jpg'
}
];
}
Kodu dikkatli kullanın.
Adım 4: Veriyi Alt Bileşene Aktarma ve Ekranda
Çoğaltma
Bir bileşenden diğerine veri aktarırken alt bileşende @Input() dekoratörünü kullanırız.
src/app/card/card.component.ts (Alt Bileşen)
dosyasını düzenleyin:
typescript
import { Component, Input } from '@angular/core';
@Component({
selector:
'app-card',
templateUrl:
'./card.component.html',
styleUrls:
['./card.component.scss']
})
export class CardComponent {
// Dışarıdan gelecek verileri kabul etmek
için @Input tanımlıyoruz
@Input() baslik: string = '';
@Input() kullaniciAdi: string = '';
@Input() icerik: string = '';
@Input() resimUrl: string = '';
}
Kodu dikkatli kullanın.
src/app/card/card.component.html (Alt Bileşen
Tasarımı) dosyasını düzenleyin:
html
<div class="card" style="width: 350px; margin: 10px;">
<div class="card-image">
<figure
class="image
is-4by3">
<img
[src]="resimUrl"
alt="Kart resmi" />
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">{{
baslik }}</p>
<p class="subtitle is-6">{{
kullaniciAdi }}</p>
</div>
</div>
<div class="content">
{{ icerik
}}
</div>
</div>
</div>
Kodu dikkatli kullanın.
Son olarak ana bileşenin HTML dosyasında (src/app/app.component.html) *ngFor kullanarak bu kartları döngüye sokup ekrana
basarız:
html
<div class="section">
<div class="container">
<div class="columns is-multiline">
<!-- ngFor ile her yazıyı dönüyoruz ve property
binding ile alt bileşene paslıyoruz
-->
<div class="column is-one-third" *ngFor="let yazi of blogYazilari">
<app-card
[baslik]="yazi.baslik"
[kullaniciAdi]="yazi.kullaniciAdi"
[icerik]="yazi.icerik"
[resimUrl]="yazi.resimUrl">
</app-card>
</div>
</div>
</div>
</div>
Kodu dikkatli kullanın.
Bu sayede elinizdeki veri listesi ne kadar uzun
olursa olsun, Angular sayfayı dinamik olarak inşa
eder ve kod tekrarı yapmanızı engeller.
Class Binding ve Gelişmiş Uygulamalar (Harf Eşleştirme Oyunu)
Veri bağlama teknikleri sadece metin veya
özellikleri değil, CSS sınıflarını da kapsar. Bir elementin görünümünü,
kullanıcının doğru veya yanlış işlem yapmasına göre anlık değiştirmek
istiyorsak Class Binding biçilmiş kaftandır.
Class Binding Mantığı
Bir HTML elementine belirli bir CSS sınıfını
şartlı olarak ekleyip çıkarmak için [class.sinifIsmi]="şart" yapısı
kullanılır. Şart true ise sınıf
eklenir, false ise sınıf
elementten kaldırılır.
Uygulama: Harf Eşleştirme (Yazma Hızı) Oyunu
Kullanıcıya rastgele bir cümle gösteren ve
kullanıcının girdisiyle bu cümleyi harf harf
karşılaştıran bir uygulama yapalım. Doğru yazılan harfler yeşil, yanlış
yazılanlar kırmızı, henüz yazılmayanlar ise gri renkli olacaktır.
Bu projede rastgele kelimeler üretmek için
popüler bir paket olan faker paket
mantığını kullanabiliriz. Şimdilik basitlik adına TypeScript
tarafında bir metin belirleyelim.
Adım 1: Stil Sınıflarının Belirlenmesi (src/app/app.component.scss)
scss
.pending {
color:
#7f8c8d; /* Yazılmayı bekleyen gri harf */
}
.correct {
color:
#2ecc71; /* Doğru yazılan yeşil harf */
font-weight: bold;
}
.incorrect {
color:
#e74c3c; /* Yanlış yazılan kırmızı harf */
text-decoration:
underline;
}
.container {
max-width:
600px;
margin:
50px auto;
text-align:
center;
}
.text-display {
font-size: 24px;
margin-bottom:
20px;
letter-spacing:
2px;
}
Kodu dikkatli kullanın.
Adım 2: Mantıksal Altyapı (src/app/app.component.ts)
Kullanıcının yazdığı metin ile hedef metni karşılaştırmak için dizilerin split() metodundan
yararlanarak metinleri harf dizilerine ayırırız.
typescript
import { Component }
from '@angular/core';
@Component({
selector:
'app-root',
templateUrl:
'./app.component.html',
styleUrls:
['./app.component.scss']
})
export class AppComponent {
rastgeleCumle:
string = 'Angular ile kod
yazmak çok eğlencelidir.';
kullaniciGirdisi:
string = '';
// Kullanıcı klavyeden her tuşa bastığında
girdiyi günceller
onInputAl(olay: Event) {
const
hedef = olay.target as HTMLInputElement;
this.kullaniciGirdisi
= hedef.value;
}
// Harfin durumuna göre hangi CSS sınıfının
aktif olacağını belirleyen yardımcı fonksiyon
getHarfSinifi(hedefHarf: string,
kullaniciHarfi: string) {
if
(!kullaniciHarfi) {
return
'pending'; // Kullanıcı henüz buraya gelmedi
}
return
hedefHarf === kullaniciHarfi
? 'correct' : 'incorrect';
}
}
Kodu dikkatli kullanın.
Adım 3: Görünüm Katmanı (src/app/app.component.html)
Hedef cümleyi split('') ile harflerine ayırıp ekrana basarken her harfe
kendi durumuna göre sınıf ataması yapıyoruz.
html
<div class="container">
<h2>Harf
Eşleştirme Oyunu</h2>
<div class="text-display">
<!-- Cümlenin
harflerini tek tek dönüyoruz, i parametresi bize o anki indeksi verir -->
<span
*ngFor="let harf of rastgeleCumle.split('');
let i = index"
[className]="getHarfSinifi(harf, kullaniciGirdisi[i])">
{{ harf }}
</span>
</div>
<div class="field">
<div class="control">
<input
class="input is-large"
type="text"
placeholder="Yukarıdaki
metni aynen yazmaya çalışın..."
(input)="onInputAl($event)" />
</div>
</div>
<!-- Tebrik
mesajı yapısı: Eğer girdi hedef cümleye tamamen eşitse göster -->
<div class="box has-background-success-light" *ngIf="kullaniciGirdisi === rastgeleCumle">
<h3 class="title is-4 has-text-success">🎉 Tebrikler!
Kusursuz şekilde tamamladınız.</h3>
</div>
</div>
Kodu dikkatli kullanın.
Buradaki kritik nokta kullaniciGirdisi[i] ifadesidir.
Kullanıcı yazdıkça, örneğin 5. indeksteki harf, orijinal metnin 5. indeksindeki
harf ile anlık karşılaştırılır ve rengi griden yeşile veya kırmızıya döner.
Veri
Dönüştürücüler: Angular Pipes
Veritabanından gelen veriler
her zaman son kullanıcının görmesini istediğimiz formatta olmayabilir. Örneğin
küçük harfle saklanan bir ismi baş harfleri büyük göstermek, ham bir tarihi
(2026-08-24) okunaklı hale getirmek veya sayısal verileri para birimi
simgesiyle süslemek isteyebiliriz. Bu işler için ham veriyi bozmadan ekrandaki
gösterimini değiştiren yapılara Pipe (Boru
hattı) denir.
Hazır Angular Pipe Yapıları
Angular içinde
sıklıkla kullanılan yerleşik boru hatları bulunur:
Uygulama Semantiği:
HTML içinde dikey çizgi | işaretiyle kullanılır:
html
{{ 'ahmet duran' | titlecase }} <!-- Ekrana "Ahmet Duran" yazar
-->
Kodu dikkatli kullanın.
Uygulama: Ürün Bilgi Formu ve Custom
Pipe Yapımı
Kendi özel boru hattımızı (Custom
Pipe) nasıl yapacağımızı ve hazır boru hatlarını bir
arada nasıl kullanacağımızı gösteren bir ürün ve maliyet tablosu tasarlayalım.
Adım 1: Custom Pipe Oluşturma
Metinleri belirli bir karakter sınırından sonra kesip sonuna üç nokta (...) koyan bir
boru hattı tasarlayalım. Terminale yazın:
bash
ng g p ozet
Kodu dikkatli kullanın.
(Not: p pipe kelimesinin kısaltmasıdır).
src/app/ozet.pipe.ts dosyasını şu
şekilde kodlayın:
typescript
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'ozet'
})
export class OzetPipe implements PipeTransform {
// transform metodu
girdiyi alır ve işleyip geriye döndürür
transform(deger: string,
limit: number = 20): string
{
if
(!deger) return '';
if
(deger.length <= limit) return
deger;
return
deger.substring(0, limit) + '...';
}
}
Kodu dikkatli kullanın.
Adım 2: TypeScript
Tarafında Verilerin Hazırlanması (src/app/app.component.ts)
typescript
import { Component }
from '@angular/core';
@Component({
selector:
'app-root',
templateUrl:
'./app.component.html'
})
export class AppComponent {
bugun
= new Date();
urunDetaylari
= {
isim: 'kablosuz
oyuncu faresi',
fiyat: 1250.45,
kod: 'TR_MOU_99',
aciklama:
'Bu fare ultra hafif yapısı ve yüksek hassasiyetli optik sensörü ile
profesyonel oyuncular için özel olarak tasarlanmıştır.'
};
}
Kodu dikkatli kullanın.
Adım 3: HTML Arayüzünde Pipe
Kullanımları (src/app/app.component.html)
html
<div class="box" style="max-width: 500px; margin: 30px auto;">
<h3 class="title is-5">Ürün
Kartı</h3>
<p><strong>Sisteme Giriş Tarihi:</strong>
{{ bugun | date:'dd MMMM yyyy EEEE' }}</p>
<p><strong>Ürün Adı:</strong>
{{ urunDetaylari.isim | titlecase
}}</p>
<!-- Para birimi
formatlama: ABD Doları simgesi ve kuralları -->
<p><strong>Uluslararası Fiyat:</strong>
{{ urunDetaylari.fiyat | currency:'USD':'symbol':'1.2-2'
}}</p>
<!-- Kendi
yazdığımız özel pipe kullanımı: Açıklamayı 30
karakterde kesiyoruz -->
<p><strong>Kısa Açıklama:</strong>
{{ urunDetaylari.aciklama | ozet:30 }}</p>
<!-- Debug amaçlı objenin ham halini görmek için json pipe -->
<pre>{{
urunDetaylari | json
}}</pre>
</div>
Kodu dikkatli kullanın.
Direktifler (Directives) ve Custom Directive
Oluşturma
Angular'da HTML
elemanlarının davranışlarını değiştirmek, onlara yeni yetenekler kazandırmak
veya görünümlerini manipüle etmek için Directives
(Direktifler) kullanılır.
Bileşenler (Components) aslında kendilerine ait
bir şablonu (HTML) olan direktiflerdir. Şablonu olmayan, sadece mevcut
elementlerin üzerine yerleştirilen iki ana direktif türü vardır:
NgClass ve NgSwitch Kullanımı
Uygulama: Özel Renk Değiştirici Direktif (Custom Directive)
Gelişmiş senaryo olarak üzerine fare ile
gelindiğinde arka plan rengini değiştiren, fareden uzaklaşıldığında eski haline
dönen ve dışarıdan parametre alabilen bir özellik direktifi tasarlayalım.
Adım 1: Direktif Oluşturma
Terminalde şu komutu çalıştırın:
bash
ng g d vurgula
Kodu dikkatli kullanın.
(Not: g generate, d ise directive kelimesinin kısaltmasıdır).
Adım 2: Direktif Kodlarının Yazılması (src/app/vurgula.directive.ts)
Direktiflerde elemente müdahale etmek için ElementRef yapısını,
olayları dinlemek için ise @HostListener yapısını
kullanırız.
typescript
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector:
'[appVurgula]' // HTML içinde elemente eklenecek isim
})
export class VurgulaDirective {
// Dışarıdan renk parametresi almak için input tanımlıyoruz
@Input() varsayilanRenk: string = 'yellow';
@Input() vurguRengi: string = 'cyan';
constructor(private el: ElementRef)
{}
// Fare elementin üzerine geldiğinde
tetiklenir
@HostListener('mouseenter') onMouseEnter()
{
this.renkDegistir(this.vurguRengi);
}
// Fare elementten ayrıldığında tetiklenir
@HostListener('mouseleave') onMouseLeave()
{
this.renkDegistir(null);
}
private
renkDegistir(renk: string |
null) {
this.el.nativeElement.style.backgroundColor
= renk;
}
}
Kodu dikkatli kullanın.
Adım 3: HTML Üzerinde Uygulama (src/app/app.component.html)
html
<div class="content" style="padding: 20px;">
<!-- Direktifi
yalın haliyle kullanırsak varsayılan renkleri alır -->
<p appVurgula>Bu paragrafın üzerine gelirseniz arka planı
değişir.</p>
<!-- Direktife
dışarıdan parametre geçerek özelleştiriyoruz -->
<p [appVurgula] vurguRengi="lightgreen">
Bu paragraf ise üzerine gelindiğinde açık
yeşil olur.
</p>
</div>
Kodu dikkatli kullanın.
Modüller (Modules) ve Yönlendirme (Routing) Yapısı
Büyük ölçekli projelerde yüzlerce bileşen ve
direktif bulunur. Bunların hepsini bir arada tutmak karmaşaya yol açar. Angular, projeyi mantıksal kutulara bölmek için Module (Modül) yapısını kullanır.
Modül Mantığı, Import
ve Export İşlemleri
Her Angular
uygulamasının en az bir ana modülü bulunur: AppModule. Yeni
modüller oluşturarak (örneğin Ürünler Modülü, Kullanıcı Modülü) kodları
birbirinden yalıtabiliriz. Bir modülün içindeki bileşenin başka bir modülde
kullanılabilmesi için, kendi modülünde export
(dışa aktarım) edilmesi, kullanılacak modülde ise import
(içe aktarım) edilmesi şarttır.
Sayfa Yönlendirmeleri (Routing) ve RouterLink
Kullanıcı menüden "Hakkımızda" linkine
tıkladığında sayfa tamamen yenilenmeden (Single Page Application mantığıyla) sadece orta alandaki bileşenin
değişmesini sağlayan mekanizmaya Routing denir.
Performans Canavarı: Lazy
Loading vs Eager Loading
Uygulama: Çoklu Sayfa ve Lazy
Loading Kurulumu
Senaryomuzda bir ana sayfa, bir tablo sayfası ve
hatalı linkler için "Sayfa Bulunamadı" ekranı tasarlayalım. Tablo
sayfasını tamamen ayrı bir modül yapıp sisteme tembel yükleme (lazy load) ile bağlayalım.
Adım 1: Alt Modülün ve Bileşenlerin Oluşturulması
Öncelikle yönlendirme desteği olan bağımsız bir Tablo modülü
açalım:
bash
ng g m tablo --routing
Kodu dikkatli kullanın.
Şimdi bu modülün içine ait olacak bir tablo
bileşeni üretelim:
bash
ng g c tablo/tablo-goster
Kodu dikkatli kullanın.
Adım 2: Alt Modülün Kendi İç Rotalarının
Ayarlanması (src/app/tablo/tablo-routing.module.ts)
typescript
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TabloGosterComponent } from './tablo-goster/tablo-goster.component';
const routes: Routes = [
// Bu modül çağrıldığında varsayılan olarak
tablo-goster bileşeni yüklensin
{ path:
'', component: TabloGosterComponent
}
];
@NgModule({
imports:
[RouterModule.forChild(routes)],
exports:
[RouterModule]
})
export class TabloRoutingModule { }
Kodu dikkatli kullanın.
Adım 3: Ana Rota Dosyasında Lazy
Loading Tanımı (src/app/app-routing.module.ts)
Ana yönlendirme dosyamızda hangi kelimede hangi modülün tembel yükleneceğini loadChildren mimarisiyle belirtiriz. Hatalı
URL'ler için joker karakter ** kullanırız.
typescript
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { NotFoundComponent } from './not-found/not-found.component';
const routes: Routes = [
{ path:
'', component: HomeComponent
}, // Boş adreste Ana Sayfa açılır
// Lazy Loading Tanımı: Kullanıcı tarayıcıya /tablo yazdığı an bu
modül indirilir
{
path:
'tablo',
loadChildren:
() => import('./tablo/tablo.module').then(m => m.TabloModule)
},
{ path:
'**', component: NotFoundComponent
} // Tanımlanmamış her şeyde 404 sayfası
];
@NgModule({
imports:
[RouterModule.forRoot(routes)],
exports:
[RouterModule]
})
export class AppRoutingModule { }
Kodu dikkatli kullanın.
Adım 4: Ana Menü Tasarımı ve Router
Yapısı (src/app/app.component.html)
html
<nav class="navbar is-dark">
<div class="navbar-menu">
<div class="navbar-start">
<!-- exact: true ifadesi tam eşleşme
durumunda aktif sınıfını tetikler -->
<a class="navbar-item" routerLink="/" routerLinkActive="is-active" [routerLinkActiveOptions]="{exact: true}">
Ana Sayfa
</a>
<a class="navbar-item" routerLink="/tablo" routerLinkActive="is-active">
Dinamik Tablo (Lazy)
</a>
</div>
</div>
</nav>
<div class="container main-content">
<!-- Sayfa
bileşenleri buranın içine doğacaktır -->
<router-outlet></router-outlet>
</div>
Kodu dikkatli kullanın.
Bileşen Ömür
Döngüsü (Component Lifecycle Hooks)
ve Modal Uygulaması
Bir Angular bileşeni
doğar, yaşar (üzerindeki veriler değişir) ve ölür (ekrandan kaldırılır). Bu
süreçteki kırılma anlarında kendi kodlarımızı araya sıkıştırabilmemizi sağlayan
metodlara Lifecycle
Hooks denir.
En Önemli Ömür Döngüsü Metotları
Uygulama: Akıllı Modal (Pop-up)
Ekranı
Açılıp kapanabilen, başka bir sayfaya
geçildiğinde hafızada sızıntı yapmaması için kendini imha eden (ngOnDestroy) ve kapatma bilgisini ana
bileşene fırlatan (@Output()) gelişmiş bir
modal yapalım.
Adım 1: Alt Bileşenin Kodlanması (src/app/modal/modal.component.ts)
Bileşenden dışarıya bir olay fırlatırken @Output() ve EventEmitter mekanizmasını kullanırız.
typescript
import { Component, OnInit, OnDestroy, Output, EventEmitter } from '@angular/core';
@Component({
selector:
'app-modal',
templateUrl:
'./modal.component.html',
styleUrls:
['./modal.component.scss']
})
export class ModalComponent implements OnInit, OnDestroy {
// Dışarıya kapatma sinyali gönderecek olay
tetikleyicisi
@Output() kapatmaOlayi = new EventEmitter<void>();
ngOnInit() {
console.log('Modal bileşeni DOM ağacına eklendi (Doğdu).');
}
onKapat() {
// Butona basıldığında üst bileşene haber
veriyoruz
this.kapatmaOlayi.emit();
}
ngOnDestroy() {
console.log('Modal
bileşeni ekrandan kaldırıldı (Öldü). Hafıza temizlendi.');
}
}
Kodu dikkatli kullanın.
Adım 2: Alt Bileşenin Tasarımı (src/app/modal/modal.component.html)
html
<div class="modal is-active">
<!-- Arka plana
tıklandığında da kapansın -->
<div class="modal-background" (click)="onKapat()"></div>
<div class="modal-card">
<header
class="modal-card-head">
<p class="modal-card-title">Sistem Onayı</p>
<button
class="delete" aria-label="close" (click)="onKapat()"></button>
</header>
<section
class="modal-card-body">
Değişiklikleri kaydetmek istediğinize
emin misiniz?
</section>
<footer
class="modal-card-foot">
<button
class="button is-success" (click)="onKapat()">Evet, Onayla</button>
<button
class="button" (click)="onKapat()">Vazgeç</button>
</footer>
</div>
</div>
Kodu dikkatli kullanın.
Adım 3: Ana Bileşende Modalın Yönetilmesi (src/app/app.component.html)
html
<div class="section">
<button
class="button is-primary" (click)="modalGoster = true">Onay
Penceresini Aç</button>
<!-- Şartlı
gösterim ve olay yakalama bir arada -->
<app-modal
*ngIf="modalGoster"
(kapatmaOlayi)="modalGoster
= false"></app-modal>
</div>
Kodu dikkatli kullanın.
Gelişmiş Form
Yönetimi: Reactive Forms ve Geçerlilik Kontrolleri (Validators)
Angular form
yönetiminde iki farklı yaklaşım sunar: Template-driven
(Şablon Odaklı) ve Reactive Forms (Reaktif Formlar).
Büyük, kurumsal ve karmaşık projelerde doğrulama süreçleri ile dinamik
yapıların kolay yönetimi açısından Reactive
Forms endüstrinin standart tercihidir.
Temel Kavramlar
Uygulama: Detaylı Kredi Kartı Kayıt Formu
Kullanıcının kredi kartı bilgilerini alan,
girdiler hatalıysa anlık uyarı veren ve form tamamen kusursuz dolmadan
"Kaydet" butonunu açmayan profesyonel bir arayüz kuralım.
Adım 1: Reaktif Form Modülünün Tanımlanması (src/app/app.module.ts)
Bu yapıyı kullanabilmek için projenin ana modülüne ReactiveFormsModule eklenmelidir.
typescript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms'; // İthal edilen
modül
import { AppComponent } from './app.component';
@NgModule({
declarations:
[AppComponent],
imports:
[BrowserModule, ReactiveFormsModule],
// Buraya eklenmeli
providers:
[],
bootstrap:
[AppComponent]
})
export class AppModule { }
Kodu dikkatli kullanın.
Adım 2: Form Yapısının TypeScript
Sınıfında Kurulması (src/app/app.component.ts)
typescript
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector:
'app-root',
templateUrl:
'./app.component.html',
styleUrls:
['./app.component.scss']
})
export class AppComponent implements OnInit {
kartFormu!: FormGroup;
ngOnInit() {
// Form yapısını ve doğrulama kurallarını
inşa ediyoruz
this.kartFormu
= new FormGroup({
kartSahibi:
new FormControl('', [
Validators.required,
Validators.minLength(5)
]),
kartNumarasi:
new FormControl('', [
Validators.required,
Validators.pattern('^[0-9]{16}$') // Tam olarak 16 haneli sayı olmalı
]),
sonKullanma:
new FormControl('', [
Validators.required,
Validators.pattern('^(0[1-9]|1[0-2])\\/?([0-9]{2})$') // AA/YY formatı
]),
guvenlikKodu:
new FormControl('', [
Validators.required,
Validators.pattern('^[0-9]{3}$') // 3 haneli CVV
])
});
}
// HTML tarafında hata mesajlarını temiz
göstermek için yardımcı get metodu
get
f() {
return
this.kartFormu.controls;
}
onFormGonder() {
if
(this.kartFormu.valid) {
console.log('Form
Başarıyla Gönderildi, Veriler: ', this.kartFormu.value);
// Formu sıfırlamak için reset metodu
kullanılır
this.kartFormu.reset();
}
}
}
Kodu dikkatli kullanın.
Adım 3: HTML Form Bağlantıları ve Canlı Hata
Mesajları (src/app/app.component.html)
html
<div class="box" style="max-width: 500px; margin: 40px auto;">
<h2 class="title is-4">💳 Güvenli Ödeme
Formu</h2>
<!-- formGroup ile HTML formu TypeScript'teki
gruba bağlanır -->
<form [formGroup]="kartFormu"
(ngSubmit)="onFormGonder()">
<!-- Kart
Sahibi Alanı -->
<div class="field">
<label
class="label">Kart
Sahibi Adı Soyadı</label>
<div class="control">
<input class="input" type="text" formControlName="kartSahibi"
[ngClass]="{'is-danger':
f['kartSahibi'].touched
&& f['kartSahibi'].invalid}"
/>
</div>
<!--
Kullanıcı alana dokunmuşsa (touched) ve alan
geçersizse hatayı göster -->
<p class="help is-danger" *ngIf="f['kartSahibi'].touched &&
f['kartSahibi'].errors?.['required']">
Bu alan boş bırakılamaz.
</p>
<p class="help is-danger" *ngIf="f['kartSahibi'].touched &&
f['kartSahibi'].errors?.['minlength']">
Ad soyad en
az 5 karakter olmalıdır.
</p>
</div>
<!-- Kart
Numarası Alanı -->
<div class="field">
<label
class="label">Kart
Numarası</label>
<div class="control">
<input class="input" type="text" formControlName="kartNumarasi" placeholder="1234123412341234"
/>
</div>
<p class="help is-danger" *ngIf="f['kartNumarasi'].touched &&
f['kartNumarasi'].invalid">
Lütfen 16 haneli kart numaranızı
aralarında boşluk olmadan girin.
</p>
</div>
<!-- Son
Kullanma Tarihi ve Güvenlik Kodu (Yan Yana Giriş) -->
<div class="columns">
<div class="column">
<div class="field">
<label class="label">Son Kullanma (AA/YY)</label>
<input class="input" type="text" formControlName="sonKullanma" placeholder="12/29"
/>
<p class="help is-danger" *ngIf="f['sonKullanma'].touched &&
f['sonKullanma'].invalid">Geçersiz
tarih.</p>
</div>
</div>
<div class="column">
<div class="field">
<label class="label">CVV / CVC</label>
<input class="input" type="password" formControlName="guvenlikKodu" placeholder="321"
/>
<p class="help is-danger" *ngIf="f['guvenlikKodu'].touched &&
f['guvenlikKodu'].invalid">Hatalı
kod.</p>
</div>
</div>
</div>
<!-- Buton
Aktiflik Kontrolü: Form tamamen geçerli (valid)
değilse buton kilitlenir -->
<button
class="button is-info is-fullwidth" type="submit" [disabled]="kartFormu.invalid">
Güvenli Şekilde Öde
</button>
</form>
</div>
Kodu dikkatli kullanın.
Asenkron Dünya
ve Reaktif Programlama: RxJS Kütüphanesi ve Observables
Modern web uygulamalarında veriler anlık olarak
akar. Sunucudan gelen veriler, kullanıcının sohbet kutusuna yazdığı mesajlar
veya arka planda sürekli yenilenen bildirimler zamanla değişen birer veri
akışıdır (Data Stream). Bu akışları yönetmek amacıyla
kullanılan, Angular ile tümleşik gelen güçlü
kütüphaneye RxJS adı verilir.
Temel RxJS Terimleri
Önemli RxJS
Operatörleri
RxJS, verileri
aboneye ulaşmadan önce boru hatlarında (pipe)
işleyebilmemiz için dönüştürücü operatörler sunar:
Uygulama: Gelişmiş Matematik Oyunu (RxJS Operatör Kombinasyonları)
Sistemde rastgele iki sayı üreten, kullanıcının
toplamı bulmasını isteyen ve formun geçerlilik durumunu RxJS
statusChanges observable yapısı ile anlık dinleyen dinamik bir oyun
tasarlayalım. delay ve filter operatörleriyle doğru cevap
verildiğinde sistemi sıfırlayalım.
TypeScript Kod Yapısı (src/app/app.component.ts)
typescript
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { Subscription
} from 'rxjs';
import { delay, filter, tap } from 'rxjs/operators';
@Component({
selector:
'app-root',
templateUrl:
'./app.component.html',
styleUrls:
['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
matematikFormu!: FormGroup;
sayi1: number = 0;
sayi2: number = 0;
skor: number = 0;
mesaj: string = '';
// Abonelikleri hafızadan temizlemek için
referans tutuyoruz
private
abonelik!: Subscription;
ngOnInit() {
this.soruUret();
this.matematikFormu
= new FormGroup({
cevap: new FormControl('', [Validators.required])
});
// Formun durumunun (VALID / INVALID)
değişimini anlık izliyoruz
this.abonelik
= this.matematikFormu.statusChanges.pipe(
// Sadece form geçerli (VALID) olduğunda
ilerle
filter(durum => durum === 'VALID'),
// Girilen cevabın doğruluğunu kontrol
eden filtreleme adımı
filter(() => {
const
kullaniciCevabi = parseInt(this.matematikFormu.value.cevap);
return
kullaniciCevabi === (this.sayi1 + this.sayi2);
}),
// Doğru cevap verildiğinde araya girip
skor artırma işlemi (tap)
tap(() => {
this.skor++;
this.mesaj
= '🎉 Tebrikler!
Doğru Cevap. Yeni soru geliyor...';
}),
// Kullanıcının başarıyı görmesi için 2
saniye gecikme ekliyoruz
delay(2000)
).subscribe(()
=> {
// Gecikme bittikten sonra yeni soruya
geçiş ve formu temizleme
this.soruUret();
this.matematikFormu.reset();
this.mesaj
= '';
});
}
soruUret() {
this.sayi1 = Math.floor(Math.random() * 10) + 1;
this.sayi2 = Math.floor(Math.random() * 10) + 1;
}
ngOnDestroy() {
// Hafıza sızıntısını önlemek için abonelik
iptal edilmelidir
if
(this.abonelik) {
this.abonelik.unsubscribe();
}
}
}
Kodu dikkatli kullanın.
HTML Arayüz Tasarımı (src/app/app.component.html)
html
<div class="box" style="max-width: 400px; margin: 50px auto; text-align: center;">
<h3 class="title is-3">Zeka
Oyunu</h3>
<div class="notification is-info is-light">
Skorunuz: <strong>{{ skor }}</strong>
</div>
<div class="box has-background-light">
<span class="is-size-2">{{ sayi1 }} + {{ sayi2 }} =
?</span>
</div>
<form [formGroup]="matematikFormu">
<div class="field">
<div class="control">
<input class="input is-large" type="number" formControlName="cevap" placeholder="Cevabı
yazın..." />
</div>
</div>
</form>
<p class="has-text-success is-size-5 mt-3" *ngIf="mesaj">{{
mesaj }}</p>
</div>
Kodu dikkatli kullanın.
Kurumsal
Arayüz Kütüphanesi: Angular Material
Google tarafından geliştirilen ve Material Design felsefesini taşıyan hazır, erişilebilir ve
şık bileşenler bütününe Angular Material denir. Sıfırdan buton, menü veya açılır
pencere (dialog) tasarlamak yerine bu kütüphaneyi
entegre ederek hızlıca profesyonel paneller üretebiliriz.
Yaygın Kullanılan Material
Bileşenleri
Uygulama: Material Dialog Yapısı ve Veri Tablosu Filtreleme
Bir çalışan listesi tablosu hazırlayalım. Bu
tabloya arama/filtreleme özelliği ekleyelim. Ayrıca satıra tıklandığında
detayları ekrana getiren bir Material Dialog penceresi tasarlayalım.
Adım 1: Kurulum ve Modüllerin Alınması
Terminalde Angular Material
kütüphanesini projeye dahil edin:
bash
ng add @angular/material
Kodu dikkatli kullanın.
Kurulum esnasında size tema (indigo-pink
vb.) ve yazı fontları sorulacaktır, varsayılan ayarlarla geçebilirsiniz.
Kullanacağımız modülleri src/app/app.module.ts dosyasına dahil edin:
typescript
import { MatTableModule } from '@angular/material/table';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
imports:
[
BrowserAnimationsModule,
MatTableModule, MatFormFieldModule, MatInputModule,
MatDialogModule, MatButtonModule
// ...
]
})
export class AppModule {}
Kodu dikkatli kullanın.
Adım 2: Dialog İçin
Detay Bileşeninin Hazırlanması
Terminalde detayları gösterecek alt bileşeni açın:
bash
ng g c personel-detay
Kodu dikkatli kullanın.
src/app/personel-detay/personel-detay.component.ts dosyasında
dışarıdan gelen verileri yakalamak için MAT_DIALOG_DATA enjeksiyonu
kullanılır:
typescript
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA
} from '@angular/material/dialog';
@Component({
selector:
'app-personel-detay',
template:
`
<h2 mat-dialog-title>Detaylı Bilgi
Kartı</h2>
<mat-dialog-content>
<p><strong>Personel Adı:</strong>
{{ data.isim }}</p>
<p><strong>Departman:</strong>
{{ data.departman }}</p>
<p><strong>Sicil No:</strong>
{{ data.id }}</p>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button
mat-button mat-dialog-close color="warn">Kapat</button>
</mat-dialog-actions>
`
})
export class PersonelDetayComponent {
constructor(@Inject(MAT_DIALOG_DATA)
public data: any) {}
}
Kodu dikkatli kullanın.
Adım 3: Ana Sınıfta Veri Kaynağının ve Filtreleme
Mantığının Kurulması (src/app/app.component.ts)
typescript
import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog';
import { PersonelDetayComponent } from './personel-detay/personel-detay.component';
@Component({
selector:
'app-root',
templateUrl:
'./app.component.html'
})
export class AppComponent implements OnInit {
// Tabloda listelenecek teknik sütun adları
displayedColumns:
string[] = ['id', 'isim', 'departman', 'aksiyon'];
// Veri kaynağını Material
formatına geçiriyoruz
dataSource
= new MatTableDataSource([
{ id: 101, isim: 'Ahmet
Yılmaz', departman: 'Yazılım Geliştirme' },
{ id: 102, isim: 'Ayşe
Demir', departman: 'İnsan Kaynakları' },
{ id: 103, isim: 'Mehmet
Kaya', departman: 'Pazarlama' }
]);
constructor(private dialog:
MatDialog) {}
ngOnInit() {}
// Arama kutusuna yazıldığında tetiklenen
filtreleme fonksiyonu
filtreUygula(olay: Event) {
const
filtreDegeri = (olay.target
as HTMLInputElement).value;
// Küçük/büyük harf uyumsuzluğunu önlemek
için temizleme yapıyoruz
this.dataSource.filter
= filtreDegeri.trim().toLowerCase();
}
// Satırdaki butona tıklandığında açılan
pencere mekanizması
detayAc(personel: any) {
this.dialog.open(PersonelDetayComponent, {
width:
'350px',
data: personel //
Seçilen personel bilgisini diyalog içine fırlatıyoruz
});
}
}
Kodu dikkatli kullanın.
Adım 4: HTML Tarafında Material
Tablo İskeleti (src/app/app.component.html)
html
<div style="padding: 30px; max-width: 700px; margin: 0 auto;">
<!-- Filtreleme
Giriş Alanı -->
<mat-form-field appearance="outline" style="width: 100%;">
<mat-label>Personel Ara...</mat-label>
<input
matInput (keyup)="filtreUygula($event)" placeholder="İsim veya departman yazın">
</mat-form-field>
<!-- Material Veri Tablosu -->
<table
mat-table [dataSource]="dataSource" class="mat-elevation-z8"
style="width:
100%;">
<!-- ID Sütunu
-->
<ng-container matColumnDef="id">
<th
mat-header-cell *matHeaderCellDef> No </th>
<td
mat-cell *matCellDef="let eleman"> {{eleman.id}} </td>
</ng-container>
<!-- İsim
Sütunu -->
<ng-container matColumnDef="isim">
<th
mat-header-cell *matHeaderCellDef> İsim Soyisim
</th>
<td
mat-cell *matCellDef="let eleman"> {{eleman.isim}}
</td>
</ng-container>
<!-- Departman
Sütunu -->
<ng-container matColumnDef="departman">
<th
mat-header-cell *matHeaderCellDef> Departman </th>
<td
mat-cell *matCellDef="let eleman"> {{eleman.departman}}
</td>
</ng-container>
<!-- Detay
Buton Sütunu -->
<ng-container matColumnDef="aksiyon">
<th
mat-header-cell *matHeaderCellDef> İşlemler </th>
<td
mat-cell *matCellDef="let eleman">
<button mat-raised-button color="primary" (click)="detayAc(eleman)">İncele</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns:
displayedColumns;"></tr>
</table>
</div>
Kodu dikkatli kullanın.
Tam Katmanlı
Proje Altyapısı: Angular ve .NET Core
Web API Entegrasyonu
Gerçek hayattaki kurumsal uygulamalar verilerini
kendi içlerinde geçici olarak saklamazlar. Arka planda bir veri tabanı (SQL
Server, PostgreSQL vb.) ve bu veri tabanına erişimi
sağlayan güvenli bir web servisi (API) bulunur. Sektörde en sık kullanılan
güçlü ikililerden biri Angular (Ön Yüz) ve ASP.NET
Core Web API (Arka Yüz) mimarisidir.
Veri İletişim Mimarisi (HttpClient
ve Servisler)
Angular tarafında backend ile iletişime geçmek için HttpClient servis kütüphanesini
kullanırız. Güvenlik protokolleri gereği arka yüz ile ön yüz farklı sunucularda
veya portlarda çalışıyorsa tarayıcı engellemesine takılırlar. Bu engeli aşmak
için .NET Core tarafında CORS Policy
(Köken Paylaşımı İzni) yapılandırılması şarttır.
.NET Core Tarafında Temel Yapıtaşları
Verilen eğitim müfredatını belirttiğiniz
kurallara göre satır satır ve başlık başlık ele aldım. İstediğiniz gibi süreleri, önizleme ve ders yazılarını tamamen temizledim. Konuları
mantıksal bir sırayla tasarlayıp genişlettim. Kod örnekleri, pratik uygulamalar
ve detaylı açıklamalar ekleyerek tam bir başvuru kılavuzu hazırladım.
"Öğrenecekleriniz"
Bölümünün Açıklaması
Bu eğitim paketi, modern web uygulamaları
geliştirmek için kullanılan Angular framework’ünü (altyapısını) sıfırdan başlayarak uzmanlık
seviyesine kadar öğretmeyi amaçlar. Bu bölümde hedeflenen kazanımlar şunlardır:
KURULUMLAR
Herhangi bir modern frontend
(ön yüz) projesi geliştirmek için bilgisayarımızın kodları anlayacak ve
çalıştıracak yazılımlara ihtiyacı vardır.
Node.js Kurulumu
Node.js, JavaScript kodlarının tarayıcı dışında,
yani bilgisayarımızın doğrudan kendi işletim sisteminde çalışmasını sağlayan
bir ortamdır. Angular projelerinin bağımlılıklarını
(kütüphanelerini) indirmek için Node.js ile birlikte gelen npm
(Node Package Manager)
sistemini kullanırız.
Angular CLI Kurulumu
Angular CLI (Command Line Interface),
komut satırı üzerinden hızlıca Angular projeleri
üretmemizi, yeni bileşenler veya servisler eklememizi sağlayan resmi bir
araçtır.
·
npm install -g @angular/cli
Visual Studio Code Kurulumu
Kodlarımızı rahatça yazmak, renklendirmek ve
klasör yapısını yönetmek için kullanılan ücretsiz ve popüler bir kod
editörüdür. Angular eklentileriyle (Angular Language Service) kod yazımını hızlandırır.
GİRİŞ VE TEMEL
KAVRAMLAR
Bu bölümde Angular
dünyasının temel taşlarını ve ilk projenin nasıl ayağa kaldırılacağını
inceliyoruz.
Angular Nedir ve Single Page Application (SPA)
Mantığı
Angular, Google
tarafından geliştirilen açık kaynaklı bir web uygulama platformudur. Single Page Application
(Tek Sayfa Uygulaması) mimarisini kullanır.
TypeScript Nedir?
TypeScript, JavaScript
dilinin üzerine inşa edilmiş, ona "tip desteği" (hata yakalama
koruması) ekleyen bir dildir. Yazdığınız TypeScript
kodları derlenerek standart JavaScript çıktısına dönüşür.
·
let kullaniciAdi: string = "Ahmet";
·
// kullaniciAdi = 123; // HATA! TypeScript
derleme aşamasında bu hatayı yakalar.
Proje Oluşturma ve Klasör Yapısı
·
ng new ilk-uygulama
Projeyi Ayağa Kaldırma
·
ng serve --open
BİLEŞENLER
(COMPONENT) VE VERİ BAĞLAMA (DATA BINDING)
Angular uygulamaları,
yapboz parçaları gibi bir araya gelen bileşenlerden (Component) oluşur.
Component Nedir ve Nasıl Oluşturulur?
Bir bileşen; HTML (görüntü), CSS (stil) ve TypeScript (mantık/kod) dosyalarının birleşimidir. Angular 17 ve sonrasında bileşenler varsayılan olarak standalone (bağımsız) gelir, yani başka bir
modüle kayıt edilmeden direkt kullanılabilirler.
·
ng generate component home
(Kısaca ng g c home da
yazılabilir). Bu komut home.component.ts, home.component.html, home.component.css dosyalarını
otomatik üretir.
Değişkenleri HTML'de
Görüntüleme (Interpolation)
TypeScript dosyasında
tanımlanan verileri HTML sayfasında göstermek için çift süslü parantez {{ }} kullanılır.
·
export class AppComponent {
·
kursAdi: string = 'Angular Geliştirme Eğitimi';
·
ogrenciSayisi: number = 5000;
·
}
·
<h1>{{ kursAdi }}</h1>
·
<p>Mevcut Öğrenci Sayısı: {{ ogrenciSayisi
}}</p>
Property Binding (Özellik Bağlama)
HTML elementlerinin özelliklerine (örneğin bir
butonun disabled durumu veya bir resmin src adresi) dinamik değer atamak için köşeli
parantez [] kullanılır.
·
resimUrl: string = 'logo.png';
·
butonKilitli: boolean = true;
·
<img [src]="resimUrl" />
·
<button [disabled]="butonKilitli">Tıkla</button>
Event Binding (Olay Bağlama)
Kullanıcının yaptığı etkileşimleri (tıklama,
klavyeden tuşa basma vb.) yakalayıp TypeScript
fonksiyonlarını tetiklemek için normal parantez () kullanılır.
·
<button (click)="selamla()">Merhaba
De</button>
·
selamla() {
·
alert('Angular
uygulamasından selamlar!');
·
}
ngClass ve ngStyle
Dinamik olarak CSS sınıfları veya stilleri
eklemek için kullanılırlar.
·
<div [ngStyle]="{'color': hataVar ? 'red' : 'green',
'font-size': '20px'}">
·
Durum Mesajı
·
</div>
Two-Way Data Binding (İki Yönlü Veri Bağlama)
Kullanıcının input
(giriş) alanına yazdığı verinin anında TypeScript
değişkenini güncellemesi, aynı şekilde TypeScript'teki
değişimin de inputa yansımasıdır. ngModel yapısı ile kurulur ve FormsModülünün
import edilmesi gerekir.
·
<input [(ngModel)]="kullaniciNotu" type="text" />
·
<p>Yazdığınız not: {{ kullaniciNotu }}</p>
DÖNGÜLER VE
ŞART BLOKLARI (CONTROL FLOW)
Angular'ın modern
sürümlerinde HTML içerisinde döngü ve şart kontrolü yapmak çok kolaylaşmıştır.
@if, @else if, @else
Şart Blokları
Belirli bir koşula göre HTML elementlerini
gizlemek veya göstermek için kullanılır.
·
@if (stokMiktari >
10) {
·
<span class="text-success">Stokta Var</span>
·
} @else if (stokMiktari > 0) {
·
<span class="text-warning">Son Ürünler!</span>
·
} @else {
·
<span class="text-danger">Tükendi</span>
·
}
@for Döngüsü
Bir dizi (array)
içerisindeki verileri ekrana listelemek için kullanılır. Her eleman için
benzersiz bir track anahtarı verilmesi performans için zorunludur.
·
urunler = ['Laptop', 'Telefon', 'Kulaklık'];
·
<ul>
·
@for (urun of urunler;
track urun) {
·
<li>{{ urun }}</li>
·
}
·
</ul>
YÖNLENDİRME
(ROUTING)
Çok sayfalı bir web sitesi hissi yaratmak ve URL
adresine göre doğru bileşeni ekrana getirmek için kullanılır.
Temel Routing Yapısı
·
export const routes: Routes = [
·
{ path: '', component: HomeComponent },
·
{ path: 'hakkimizda', component: AboutComponent }
·
];
·
<a routerLink="/hakkimizda">Hakkımızda Sayfasına Git</a>
Kod İçerisinden Navigasyon (Router
Service)
Butona tıklandığında veya bir işlem bittiğinde
kullanıcısı başka sayfaya yönlendirmek için Router servisi
enjekte edilir.
·
import { Router } from '@angular/router';
·
//...
·
private router = inject(Router);
·
·
sayfayaGit() {
·
this.router.navigate(['/hakkimizda']); // Kod ile
yönlendirme
·
}
Parametreli Routing
URL üzerinden veri göndermek için kullanılır (Örn: urun/5 id'li ürünü detay sayfasında açmak).
·
import { ActivatedRoute } from '@angular/router';
·
//...
·
private route = inject(ActivatedRoute);
·
·
ngOnInit() {
·
this.route.paramMap.subscribe(params => {
·
let urunId = params.get('id');
·
console.log("Gelen Ürün ID:", urunId);
·
});
·
}
DIRECTIVE
(DİREKTİFLER) VE VALIDATION
Direktifler, HTML elementlerinin davranışlarını
veya görünümlerini değiştiren özel kod bloklarıdır.
Validation Directive
(Doğrulama Direktifi)
Giriş alanlarına girilen verilerin kurallara
uygun olup olmadığını gerçek zamanlı denetlemek için yazılır.
SERVİSLER
(SERVICES) VE BİLEŞENLER ARASI İLETİŞİM
Bileşenler geçicidir; sayfa değiştiğinde içindeki
veriler yok olur. Kalıcı veriler ve ortak iş mantıkları Servislerde tutulur.
Service Nedir?
Uygulama ayakta kaldığı sürece çalışan, merkezi
veri depolarıdır. inject() fonksiyonu ile istenen
bileşene dahil edilir.
·
ng generate service veri
·
@Injectable({ providedIn: 'root' })
·
export class VeriService {
·
sepet Listesi: string[] = [];
·
·
sepeteEkle(urun: string) {
·
this.sepetListesi.push(urun);
·
}
·
}
Input / Output ile State Management
(Durum Yönetimi)
İç içe geçmiş alt ve üst bileşenlerin (Parent-Child) veri paylaşması işlemidir.
VERİ
DÖNÜŞTÜRÜCÜLER (PIPES)
HTML şablonlarında verilerin kullanıcıya
gösterilmeden önce biçimlendirilmesini sağlar. Orijinal veriyi değiştirmez,
sadece görüntüyü düzenler.
Hazır Pipe Yapıları
Angular içinde birçok
hazır dönüştürücü barındırır:
·
<p>{{ 'merhaba dunya' | uppercase }}</p> <!-- Çıktı: MERHABA DUNYA -->
·
<p>{{ paraMiktari | currency:'TRY':'symbol':'1.2-2' }}</p> <!-- Çıktı: ₺1,250.00 -->
GÜVENLİK VE
KORUYUCULAR (GUARDS)
Kullanıcıların yetkisi olmayan sayfalara (Örn: Giriş yapmadan yönetim paneline) girmesini engellemek
için kullanılan mekanizmadır.
CanActivate ve CanActivateChild
Bir sayfaya giriş izni olup olmadığını kontrol
eder. Genellikle tarayıcı hafızasındaki (localStorage)
giriş anahtarına (token) bakar.
·
export const authGuard: CanActivateFn = (route, state) => {
·
const router = inject(Router);
·
const token = localStorage.getItem('token'); // Giriş kontrolü
·
·
if (token) {
·
return true; // Geçişe izin ver
·
} else {
·
router.navigate(['/login']); // Giriş sayfasına fırlat
·
return false; // Geçişi engelle
·
}
·
};
CanDeactivate
Kullanıcının form doldururken yanlışlıkla sayfayı
kapatmasını veya geri gitmesini önlemek için "Değişiklikleriniz
kaybolabilir, çıkmak istiyor musunuz?" uyarısı çıkaran guard
yapısıdır.
VERİ SAKLAMA
(LOCALSTORAGE)
Tarayıcı kapatılsa bile verilerin kullanıcının
bilgisayarında saklanmasını sağlayan anahtar-değer (key-value)
mekanizmasıdır.
YENİ NESİL
REAKTİVİTE: SIGNALS
Angular'ın en güçlü
modern özelliklerinden biri olan Signals
(Sinyaller), değer değişimlerini en ince ayrıntısına kadar takip eden ve
sadece değişen elementi güncelleyen performans canavarı bir reaktif sistemdir.
[1]
signal() Kullanımı
Değiştirilebilir (writable)
bir sinyal oluşturur [source: 1.2.33]. Değeri okumak
için fonksiyon gibi çağrılır () [source: 1.2.11].
·
sayac = signal(0);
·
·
arttir() {
·
this.sayac.update(eskiDeger => eskiDeger
+ 1); // Güvenli güncelleme
·
}
computed() Kullanımı
Başka sinyallere bağlı, sadece o sinyaller
değiştiğinde otomatik olarak yeniden hesaplanan salt okunur (read-only) sinyallerdir. [1]
·
fiyat = signal(100);
·
adet = signal(2);
·
toplamTutar = computed(() => this.fiyat() * this.adet());
linkedSignal() Kullanımı
Bir kaynak sinyale bağlı olan ama aynı zamanda
dışarıdan el ile de güncellenebilen esnek sinyal yapılarıdır. [2, 3]
resource() ve httpResource()
Angular'ın asenkron API
isteklerini (HTTP) sinyal dünyasına entegre eden modern mekanizmasıdır. Bir
veriyi uzak sunucudan çekerken yükleniyor (loading), hata (error) ve değer (value) durumlarını
bize otomatik sinyal olarak verir [source: 1.2.12].
[5, 6]
Signal Forms (Sinyal
Tabanlı Formlar)
Angular 21 ile gelen
deneysel ve devrimsel form yapısıdır. Klasik form grupları ve modülleri yerine,
formun her bir alanını ve doğrulama kuralını tamamen sinyaller üzerinden
yönetmemizi sağlar. Boilerplate (gereksiz kod
kalabalığı) yaratmaz ve anlık validasyon kontrollerini aşırı hızlandırır. [7,
8, 9]
DIŞ DÜNYA İLE
BAĞLANTI: API İSTEKLERİ (HTTPCLIENT)
Uygulamamızın uzak bir sunucudaki veri tabanıyla
konuşmasını sağlayan modüldür.
HttpClient ile GET ve
POST İstekleri
·
import { HttpClient } from '@angular/common/http';
·
//...
·
private http = inject(HttpClient);
·
·
verileriGetir() {
·
return this.http.get('https://example.com');
·
}
Interceptors
(Seçiciler/Müdahaleciler)
Giden tüm HTTP isteklerini veya gelen tüm
yanıtları merkezi bir yerden yakalayıp değiştiren yapılardır.
·
const authInterceptor = (req, next) => {
·
const token = localStorage.getItem('token');
·
const kopyalananIstek = req.clone({
·
setHeaders: { Authorization: `Bearer ${token}` }
·
});
·
return next(kopyalananIstek);
·
};
GELİŞMİŞ
PERFORMANS: LAZY LOADING
Büyük web uygulamalarında tüm sayfaların
kodlarını tek bir seferde yüklemek sitenin ilk açılışını çok yavaşlatır. Lazy Loading (Tembel
Yükleme), bir sayfanın kodlarının sadece kullanıcı o sayfaya tıkladığı anda
indirilmesini sağlar.
·
export const routes: Routes = [
·
{
·
path: 'admin',
·
loadComponent: () => import('./admin/admin.component').then(m => m.AdminComponent)
·
}
·
];
Böylece kullanıcı Admin paneline tıklamadığı
sürece admin panelinin ağır kodları bilgisayarına yüklenmez, internetten
tasarruf edilir.
GELİŞMİŞ PROJE
MİMARİSİ: NX MONOREPO
Birden fazla projeyi (Örneğin: e-Ticaret müşteri
sitesi, e-Ticaret yönetim paneli ve ortak kullanılan tasarım kütüphanesi) tek
bir devasa klasör (Workspace) altında yönetme
mimarisidir. [10]
BİTİRME
PROJESİ UYGULAMASI: E-TİCARET UYGULAMASI
Eğitim boyunca öğrenilen tüm bu teorik ve pratik
bilgileri birleştiren devasa bir e-Ticaret uygulaması inşa edilir. Bu pratik
uygulama şu aşamalardan oluşur:
Harika bir seçim! Angular
dünyasının iki güçlü silahını, yani Signals
(yeni nesil reaktivite) ve HttpClient Interceptor (merkezi HTTP yönetimi) yapılarını adım
adım kodlayarak canlıya hazır birer uygulama haline getirelim.
Aşağıdaki kod tasarımlarını tamamen modern Angular standartlarına (Standalone
bileşenler ve fonksiyonel yapılar) uygun olarak hazırladım.
1. SIGNALS
REAKTİVİTE YAPISI (Canlı Uygulama Tasarımı)
Bu uygulamada dinamik bir Sepet ve Ürün
Yönetimi senaryosu kuracağız. Bir sinyal değiştiğinde ona bağlı olan diğer
hesaplamaların (computed) ve yan etkilerin (effect) nasıl
otomatik tetiklendiğini göreceksiniz.
TypeScript Dosyası (sepet.component.ts)
import { Component, signal, computed, effect } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
// Ürün modeli tanımı
interface Urun {
id: number;
ad: string;
fiyat: number;
}
@Component({
selector: 'app-sepet',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './sepet.component.html',
styleUrls: ['./sepet.component.css']
})
export class SepetComponent {
// 1.
Değiştirilebilir Sinyaller (Writable Signals)
urunler = signal<Urun[]>([
{ id: 1, ad: 'Kablosuz
Kulaklık', fiyat: 1500 },
{ id: 2, ad: 'Oyuncu
Faresi', fiyat: 800 }
]);
sepetAdedi = signal<number>(1);
secilenUrunId = signal<number>(1);
// 2. Otomatik
Hesaplanan Sinyal (Computed Signal)
// Seçilen
ürünün ID'sine göre ürün nesnesini bulur.
secilenUrun = computed(() => {
return this.urunler().find(u => u.id === Number(this.secilenUrunId()));
});
// 3. Başka
Sinyallere Bağlı Hesaplanan Sinyal (Computed Signal)
// Fiyat ve
adet sinyallerinden biri değiştiğinde toplam tutarı anında günceller.
toplamTutar = computed(() => {
const urun = this.secilenUrun();
return urun ? urun.fiyat * this.sepetAdedi() : 0;
});
constructor() {
// 4. Yan Etki
Takibi (Effect)
// Sinyal
değerleri her değiştiğinde arka planda (Örn:
Raporlama veya Loglama için) otomatik çalışır.
effect(() => {
console.log(`[LOG] Hesaplama Güncellendi! Seçilen Ürün: ${this.secilenUrun()?.ad}, Adet: ${this.sepetAdedi()}, Toplam: ${this.toplamTutar()} TL`);
});
}
// Sinyal
değerini güvenli bir şekilde güncelleme metodu
adetGuncelle(islem: 'arttir' | 'azalt') {
if (islem === 'arttir') {
this.sepetAdedi.update(mevcut => mevcut + 1);
} else if (islem === 'azalt' && this.sepetAdedi() > 1) {
this.sepetAdedi.update(mevcut => mevcut - 1);
}
}
}
HTML Şablonu (sepet.component.html)
<div class="container mt-4 p-4 border rounded bg-light">
<h2>Sinyal (Signals) ile
Dinamik Sepet</h2>
<hr />
<!-- Ürün Seçim Alanı -->
<div class="mb-3">
<label class="form-label">Ürün
Seçiniz:</label>
<!-- select
içindeki veri değiştikçe secilenUrunId sinyalini
günceller -->
<select class="form-select" [ngModel]="secilenUrunId()" (ngModelChange)="secilenUrunId.set($event)">
@for (item of urunler();
track item.id) {
<option [value]="item.id">{{ item.ad }} ({{ item.fiyat }}
TL)</option>
}
</select>
</div>
<!-- Seçilen Ürün Detayları -->
@if (secilenUrun()) {
<div class="card p-3 mb-3 bg-white">
<h5>Seçilen Ürün: <span class="text-primary">{{ secilenUrun()?.ad }}</span></h5>
<p>Birim
Fiyatı: <strong>{{ secilenUrun()?.fiyat
}} TL</strong></p>
<!-- Adet Kontrol Butonları -->
<div class="d-flex align-items-center
gap-2">
<span>Adet:</span>
<button class="btn btn-sm btn-danger" (click)="adetGuncelle('azalt')">-</button>
<!-- Sinyal değerini ekranda okumak
için fonksiyon gibi () çağırıyoruz -->
<span class="fs-5 fw-bold px-2">{{ sepetAdedi() }}</span>
<button class="btn btn-sm btn-success" (click)="adetGuncelle('arttir')">+</button>
</div>
</div>
<!-- Ödeme Özeti Bölümü -->
<div class="alert alert-info d-flex justify-content-between align-items-center">
<span class="fs-5">Ödenecek Toplam Tutar:</span>
<!-- toplamTutar
sinyali otomatik değişir -->
<span class="fs-4 fw-bold text-success">{{ toplamTutar() }}
TL</span>
</div>
}
</div>
2. HTTPCLIENT
İLE INTERCEPTOR YAZIMI (Merkezi HTTP Yönetimi)
Bu uygulamada ise modern Angular
(v15+) mimarisine uygun Fonksiyonel Interceptor
tasarımı yapacağız. Amacımız, giden her HTTP isteğine tarayıcı hafızasındaki (localStorage) güvenlik anahtarını (Token)
otomatik eklemek ve gelen hataları merkezi olarak yakalamaktır.
Interceptor Tanımı (auth.interceptor.ts)
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, throwError } from 'rxjs';
import { Router } from '@angular/router';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const router = inject(Router);
// 1. Tarayıcı
hafızasından Token bilgisini oku
const token = localStorage.getItem('token');
// 2. İstek (Request) nesnesini klonla ve eğer token
varsa Header (başlık) kısmına ekle
let duzenlenmisIstek = req;
if (token) {
duzenlenmisIstek
= req.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log(`[INTERCEPTOR] Giden isteğe Token eklendi: ${req.url}`);
}
// 3.
Düzenlenmiş isteği bir sonraki adıma (sunucuya) gönder ve gelen hataları dinle
return next(duzenlenmisIstek).pipe(
catchError((error: HttpErrorResponse) => {
// 4. Merkezi
Hata Yakalama (Global Error Handling)
if (error.status === 401) {
console.error('[INTERCEPTOR] Yetkisiz Giriş! Oturum sonlandırılıyor...');
localStorage.removeItem('token'); // Eski token'ı
temizle
router.navigate(['/login']); // Giriş sayfasına yönlendir
} else if (error.status === 500) {
alert('Sunucu tarafında geçici bir arıza
oluştu. Lütfen daha sonra tekrar deneyiniz.');
}
// Hatayı
bileşene (component) fırlat ki orada da özel bir
işlem yapılabilsin
return throwError(() => error);
})
);
};
Uygulamaya Entegre Etme (app.config.ts)
Yazdığımız fonksiyonel interceptor
yapısının tüm uygulamada geçerli olması için provideHttpClient içerisine
kaydedilmesi gerekir.
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
// HttpClient kütüphanesini interceptor
özelliğimiz ile birlikte uygulamaya sunuyoruz
provideHttpClient(
withInterceptors([authInterceptor])
)
]
};
API İstek Testi (urun.service.ts)
Artık yazdığınız hiçbir serviste el ile Header eklemenize gerek kalmaz. Temiz ve jenerik kod
yazabilirsiniz:
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class UrunService {
private http = inject(HttpClient);
private apiUrl = 'https://orneketicaret.com';
// Bu metot
çağrıldığında interceptor araya girecek ve Token'ı otomatik ekleyecektir!
urunleriGetir() {
return this.http.get(this.apiUrl);
}
}