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:

  1. TypeScript Dosyası (.ts): Bileşenin beynidir. Verileri ve mantıksal işleri tutar.
  2. HTML Dosyası (.html): Bileşenin iskeletidir. Ekranda ne görüneceğini belirler.
  3. Stil Dosyası (.scss/.css): Bileşenin elbisesidir. Sadece o bileşene özel görünümleri düzenler.

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:

  1. Structural Directives (Yapısal): DOM yapısını değiştirir (*ngIf, *ngFor, ngSwitch).
  2. Attribute Directives (Özellik): Öğenin görünümünü veya davranışını değiştirir (ngClass, ngStyle).

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:

  1. Mimari Kurulum: Nx Monorepo ile ana çatı kurulur, Admin Uygulaması ve Kullanıcı Arayüzü (UI) uygulamaları ayrıştırılır.
  2. Arayüz Giydirme ve Düzenleme: Hazır HTML/CSS şablonları Angular bileşenlerine bölünür (NavbarComponent, SidebarComponent, FooterComponent).
  3. Mock (Sahte) Backend: json-server paketi kullanılarak yerel bilgisayarda post, get, put, delete işlemlerine yanıt veren canlı benzeri sahte bir REST API kurulur.
  4. Gelişmiş Listeleme (Flexi UI): Açık kaynaklı flexi-grid bileşeni entegre edilerek ürün listesinde sayfalama, filtreleme, arama ve Excel'e çıktı alma özellikleri kodlanır.
  5. Hata Yönetimi (Error Interceptor): API'den dönen 400 (Hatalı İstek) veya 500 (Sunucu Hatası) durum kodlarını yakalayıp kullanıcıya şık flex-toast mesajları gösteren merkezi sistem kurulur.
  6. Kullanıcı Kayıt ve Giriş: Form validasyonları ile donatılmış Register ve Login sayfaları kodlanır. Başarılı girişte Token bilgisi localStorage üzerinde saklanır ve AuthGuard ile yetkisiz geçişler engellenir.
  7. Sepet ve Ödeme Akışı:

 

 

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);

  }

}