Bu kılavuzda, gönderdiğiniz Complete Angular 22 - Ultimate Guide
kurs içeriğini satır satır inceleyip modern yazılım
geliştirme dünyasındaki karşılıklarını, teorilerini ve pratik uygulamalarını
çıkaracağız.
İlk olarak, kursun vaat ettiği hedefleri içeren
bölümün ne anlama geldiğini açıklayarak başlayalım.
"Öğrenecekleriniz"
Bölümünün Açıklaması
Bu başlık altındaki maddeler, kurs bittiğinde
kazanacağınız profesyonel yetenekleri gösterir:
BÖLÜM 1: Giriş
ve Temel Yapı Tasarımı (Introduction & Basics)
Bu bölüm, kod yazmaya başlamadan önce Angular'ın arkasındaki fabrikayı anlamamızı sağlar.
1. Angular Nedir ve
Amaçları Nelerdir?
Angular, Google
tarafından geliştirilen büyük ve kurumsal web uygulamaları üretmeye yarayan bir
framework (yazılım iskeleti) sistemidir. Amacı,
kodların düzenli olmasını sağlamak, hızlı çalışan sayfalar üretmek ve ekiplerin
aynı kurallarla kod yazmasını kolaylaştırmaktır. [1, 2]
2. Angular Kod Derleme
Süreci (Code Compilation Process)
Tarayıcılar (Chrome, Edge
vb.) Angular kodunu (TypeScript)
doğrudan okuyamaz. Angular derleyicisi (Compiler),
yazdığımız modern kodları tarayıcının anlayacağı saf JavaScript diline çevirir.
Proje yayına alınırken bu işlem önceden yapılır (AoT - Ahead
of Time Compilation), böylece web sitesi açılır
açılmaz çok hızlı yüklenir.
3. Ana Yapı Taşları ve Mimari (Building Blocks & Architecture)
Angular üç ana
parçadan oluşur:
4. Uygulama Klasör Yapısı (App
Folder Structure)
Büyük projelerde kaybolmamak için kodlar belirli
bir düzende tutulur.
BÖLÜM 2: TypeScript Temelleri (TypeScript
Essentials)
TypeScript, JavaScript
dilinin daha güvenli ve akıllı halidir. Hataları daha kod yazarken yakalamamızı
sağlar.
1. Veri Tipleri (Data Types)
ve Union Types
TypeScript'te bir
değişkenin ne saklayacağını önceden söyleriz.
// Temel Tipler
let kullaniciAdi: string = "Ahmet";
let yas: number = 25;
let aktifMi: boolean = true;
// Union Types (Ya o, ya bu olabilir)
let telefon: string | number;
telefon = "0555...";
telefon = 555123; // İkisi de
geçerlidir.
2. Diziler (Arrays) ve Tuples
Birden fazla veriyi listelemek için kullanılır. Tuple ise eleman sayısı ve tipleri sabit olan özel
dizilerdir.
let alisverisListesi: string[] = ["Elma", "Armut", "Muz"];
// Tuple
Örneği (İlk eleman string, ikinci eleman sayı olmak
zorunda)
let kullanıcıKutusu: [string, number] = ["Admin", 1];
3. Nesne Kalıpları (Object Literals)
ve Type Aliases
Kendi özel veri tipimizi oluşturmamızı sağlar.
// Type Alias ile şablon oluşturma
type Gorev = {
id: number;
baslik: string;
tamamlandi: boolean;
};
// Şablonu kullanma
let yeniGorev: Gorev = {
id: 1,
baslik: "Angular Çalış",
tamamlandi: false
};
4. Fonksiyonlar ve Arrow
Functions
İş yapan kod bloklarıdır. Ok fonksiyonları (Arrow Functions) daha kısa yazım
sunar.
// Normal Fonksiyon
function topla(a: number, b: number): number {
return a + b;
}
// Arrow
Fonksiyon
const carp = (x: number, y: number): number => x * y;
5. Rest Parameters,
Spread Operator ve Destructuring
Verileri parçalamak veya birleştirmek için
kullanılan sihirli araçlardır.
// Spread (Dağıtma/Birleştirme)
let eskiSayilar = [1, 2];
let yeniSayilar = [...eskiSayilar, 3, 4]; // [1, 2, 3, 4] olur.
// Destructuring
(Kutudan çıkarma)
const ogrenci = { isim: "Can", not: 90 };
const { isim, not } = ogrenci; // Artık
doğrudan 'isim' yazarak erişebiliriz.
6. Sınıflar (Classes), Constructors ve Access Modifiers
Nesne yönelimli programlamanın kalbidir. Sınıflar
bir nesnenin fabrikası gibidir.
class Araba {
// Access Modifiers (Erişim Belirteçleri): public
veya private
private motorGucu: number;
public marka: string;
// Constructor (Yapıcı Metot): Sınıf oluşturulurken ilk
çalışan yer
constructor(markaAdi: string, guc: number) {
this.marka = markaAdi;
this.motorGucu = guc;
}
}
let benimArabam = new Araba("Toyota", 110);
BÖLÜM 3: İlk Angular Uygulaması ve Pratik Kodlama (First App in Angular)
1. Angular CLI Kurulumu
ve Proje Oluşturma
Komut satırından Angular
projesi üretmemizi sağlayan aracı bilgisayara kurarız.
# Kurulum komutu
npm install -g @angular/cli
# Yeni proje oluşturma
ng new ilk-projem
# Projeyi ayağa kaldırma ve
tarayıcıda açma
cd ilk-projem
ng serve --open
2. Bileşenleri (Components) Anlamak
Bileşenler web sayfasının hücreleridir. Bir
bileşen 3 ana parçadan oluşur: HTML (görüntü), CSS (stil) ve TypeScript (akıl/mantık).
// merhaba.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-merhaba',
standalone: true,
template: `<h1>Merhaba Dünya! Benim adım {{ isim
}}</h1>`,
styles: [`h1 { color: blue; }`]
})
export class MerhabaComponent {
isim: string = 'Angular Geliştiricisi';
}
3. Bootstrapping Process (Açılış Süreci)
Uygulama ilk açıldığında main.ts dosyası tetiklenir. Bu dosya Angular
motorunu çalıştırır ve kök bileşen olan AppComponent yapısını
index.html içindeki <app-root></app-root> etiketinin
içine yerleştirir.
4. Temel Yönlendirme (Basic Routing)
Sayfalar arası geçişi sağlar. Kullanıcı üst
menüden "Hakkımızda" linkine tıkladığında sayfa yenilenmeden ilgili
bileşen ekrana gelir.
// app.routes.ts
import { Routes } from '@angular/router';
import { AnasayfaComponent } from './anasayfa/anasayfa.component';
import { IletisimComponent } from './iletisim/iletisim.component';
export const routes: Routes = [
{ path: '', component: AnasayfaComponent },
{ path: 'iletisim', component: IletisimComponent }
];
BÖLÜM 4: Veri
Bağlama Yöntemleri (Data Bindings)
TypeScript dosyasındaki
verileri HTML ekranına aktarma ve ekrandaki hareketleri yakalama yöntemleridir.
1. Interpolation (Yazı
Ekleme) ve Property Binding
(Özellik Bağlama)
<!-- Interpolation -->
<p>Hoş geldin, {{ kullanici }}</p>
<!-- Property Binding
-->
<button [disabled]="butonKilitliMi">Tıkla</button>
2. Event Binding (Olay Yakalama) ve Two-Way
Binding (Çift Yönlü Bağlama)
<!-- Event Binding -->
<button (click)="girişYap()">Giriş</button>
<!-- Two-Way Binding -->
<input [(ngModel)]="aramaMetni" placeholder="Ara...">
<p>Aranan kelime: {{ aramaMetni }}</p>
3. Stil ve Klas Bağlama (Style & Class Binding)
Şarta bağlı olarak elementlerin renklerini veya
CSS sınıflarını değiştirmemizi sağlar.
<!-- Şart doğruysa arka planı kırmızı yap -->
<div [style.background-color]="hataVarMi ? 'red' : 'green'">Mesaj</div>
<!-- Şart doğruysa 'aktif' CSS sınıfını yükle -->
<div [class.aktif]="sayfaSeciliMi">Kart İçeriği</div>
4. Yeni Nesil Kontrol Blokları (@if, @for,
@switch)
Angular 22 ile
tamamen kararlı hale gelen, HTML içinde döngü ve şart yazmayı kolaylaştıran
modern yapılardır. [3, 4]
<!-- @if ve @else kullanımı -->
@if (yukleniyor) {
<p>Veriler
getiriliyor...</p>
} @else {
<p>Veriler
yüklendi!</p>
}
<!-- @for kullanımı (Döngü) -->
<ul>
@for (urun of urunler;
track urun.id; let i = $index) {
<li>{{ i + 1 }} - {{ urun.ad }}</li>
}
</ul>
BÖLÜM 5: Görev
Yöneticisi Uygulaması Ödevi (Task Manager Project - Lab)
Öğrendiğimiz veri bağlama ve kontrol bloklarını
pekiştirmek için bir Görev Yöneticisi tasarlayalım.
Uygulama Adımları:
// task-manager.component.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-task-manager',
standalone: true,
imports: [FormsModule],
template: `
<input [(ngModel)]="yeniGorevBasligi"
placeholder="Görev yaz...">
<button (click)="gorevEkle()">Ekle</button>
@for (g of gorevler; track
g.id) {
<div [style.text-decoration]="g.bitti
? 'line-through' : 'none'">
<input type="checkbox" [(ngModel)]="g.bitti">
{{ g.baslik }}
</div>
}
`
})
export class TaskManagerComponent {
yeniGorevBasligi = '';
gorevler =
[
{ id: 1, baslik: 'Market Alışverişi', bitti: false },
{ id: 2, baslik: 'Kod İnceleme', bitti: true }
];
gorevEkle() {
if(this.yeniGorevBasligi.trim()) {
this.gorevler.push({
id: Date.now(),
baslik: this.yeniGorevBasligi,
bitti: false
});
this.yeniGorevBasligi = '';
}
}
}
BÖLÜM 6: Hata
Ayıklama (Debugging)
Kodlarımız çalışmadığında nerede hata yaptığımızı
bulma sanatıdır.
BÖLÜM 7:
Servisler ve Bağımlılık Enjeksiyonu (Services & DI)
Bileşenlerin sadece görüntüyü yönetmesi gerekir.
Veri çekme, hesaplama gibi ağır işleri Servisler üstlenir.
1. Servis Oluşturma ve Tüketme
Bir servisi projenin her yerinde kullanabilmek
için ortak bir işçi olarak tanımlarız.
// veri.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Tüm uygulamada tek bir kopya
olarak çalışır
})
export class VeriService {
private loglar: string[] = [];
logEkle(mesaj: string) {
this.loglar.push(mesaj);
console.log("Yeni Log: " +
mesaj);
}
}
2. Bağımlılık Enjeksiyonu (Dependency
Injection
- DI)
Bileşenlerin ihtiyaç duyduğu servisleri kendi
içlerinde sıfırdan üretmek yerine, Angular motorundan
"bana bu servisi getir" diyerek talep etmesi yöntemidir. Angular 22 ile birlikte gelen modern inject fonksiyonu bu işi çok kolaylaştırır. [3, 5]
// ana.component.ts
import { Component, inject } from '@angular/core';
import { VeriService } from './veri.service';
@Component({ selector: 'app-ana', standalone: true, template: `<button (click)="islem()">Logla</button>` })
export class AnaComponent {
// Yeni ve
modern yöntem: inject kullanımı
private veriServisi = inject(VeriService);
islem() {
this.veriServisi.logEkle("Ana
sayfaya tıklandı!");
}
}
BÖLÜM 8:
Borular / Filtreler (Pipes)
Pipes, ekrandaki
verileri kullanıcının gözüne hoş gelecek şekilde anlık olarak biçimlendiren
dönüştürücülerdir. Verinin aslı değişmez, sadece görünüşü değişir.
1. Hazır Pipes
Kullanımı
Angular içinde tarih,
para birimi, büyük/küçük harf gibi dönüştürücüler hazır gelir.
<!-- Yazıyı büyük harfe çevirir: AHMET -->
<p>{{ 'ahmet' | uppercase }}</p>
<!-- Parayı dolar formatına sokar: $100.00 -->
<p>{{ 100 | currency }}</p>
2. Özel Pipe (Custom Pipe) Yapımı
Kendi özel filtrelerimizi de yazabiliriz. Örneğin
gelen metni kısaltan bir özetleme borusu yapalım:
// ozet.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Component({ }) // Pipe tanımlaması
@Pipe({
name: 'ozet',
standalone: true
})
export class OzetPipe implements PipeTransform {
transform(value: string, limit: number = 10): string {
if (!value) return '';
return value.length > limit ? value.substring(0, limit) + '...' : value;
}
}
HTML içinde kullanımı:
<!-- Çıktı: Uzun bir m... -->
<p>{{ 'Uzun bir makale metni buraya
gelir' | ozet:10 }}</p>
BÖLÜM 9: HTTP
İletişimi ve HttpClient (HttpClient)
Uygulamamızın internetteki bir sunucuyla (REST
API) konuşmasını sağlayan modüldür.
// calisan.service.ts
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class CalisanService {
private http = inject(HttpClient);
private apiAdresi = 'https://ornek.com';
// Veri
Getirme (GET)
calisanlariGetir() {
return this.http.get(this.apiAdresi);
}
// Yeni Ekleme
(POST)
calisanEkle(yeniCalisan: any) {
return this.http.post(this.apiAdresi, yeniCalisan);
}
// Silme
(DELETE)
calisanSil(id: number) {
return this.http.delete(`${this.apiAdresi}/${id}`);
}
}
HTTP Interceptors (Yol
Kesenler)
Uygulamadan çıkan her isteğin veya gelen her
cevabın önünü kesip ortak işlemler yapmayı sağlar. Örneğin, her isteğin
arkasına güvenlik token'ı (JWT) eklemek veya hataları
merkezi bir yerde yakalamak için kullanılır.
BÖLÜM 10: Veri
Tablosu Geliştirme Laboratuvarı (Working with Data Grid)
Gerçek dünyada en çok yapılan işlerden biri,
gelen verileri sayfalı, aramalı bir tabloda göstermektir.
Gelişmiş Arama ve Sayfalama Uygulaması:
Kullanıcı arama kutusuna yazı yazdığı an sunucuya hemen istek gitmesini önlemek
için belirli bir süre bekleme (Debounce) mekanizması
eklenir.
// Veri tablosu mantığı örneği
import { Component, OnInit, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject, debounceTime, distinctUntilChanged } from 'rxjs';
@Component({
selector: 'app-employee-table',
template: `
<input (input)="onSearch($event)" placeholder="İsim
ile ara...">
<table>
@for(emp of filteredEmployees; track emp.id)
{
<tr><td>{{ emp.name }}</td></tr>
}
</table>
`
})
export class EmployeeTableComponent implements OnInit {
private http = inject(HttpClient);
private searchSubject = new Subject<string>();
employees: any[] = [];
filteredEmployees: any[] = [];
ngOnInit() {
// Sunucudan
ham veriyi çek
this.http.get<any[]>('https://api.com').subscribe(data
=> {
this.employees = data;
this.filteredEmployees = data;
});
// Arama
kutusuna yazarken 400 milisaniye bekle (Debounce)
this.searchSubject.pipe(
debounceTime(400),
distinctUntilChanged()
).subscribe(searchText => {
this.filteredEmployees = this.employees.filter(e =>
e.name.toLowerCase().includes(searchText.toLowerCase())
);
});
}
onSearch(event: any) {
this.searchSubject.next(event.target.value);
}
}
BÖLÜM 11: Form
Yönetimi (Template-Driven & Reactive
Forms)
Angular'da formlar ikiye
ayrılır: Basit formlar ve kontrollü/dinamik formlar.
1. Template-Driven
Forms (Şablon Odaklı Formlar)
Daha çok HTML ağırlıklı, basit giriş formlarında
tercih edilir. Doğrulamalar (Validation) HTML
etiketleri üzerine yazılır.
<form #kullaniciFormu="ngForm" (ngSubmit)="kaydet(kullaniciFormu.value)">
<input name="eposta" ngModel required
email #emailKutusu="ngModel">
@if (emailKutusu.invalid
&& emailKutusu.touched) {
<small style="color: red;">Geçerli bir e-posta yazınız!</small>
}
<button [disabled]="kullaniciFormu.invalid">Gönder</button>
</form>
2. Reactive Forms
(Reaktif / Dinamik Formlar)
Bütün kontrolün TypeScript
kodlarında olduğu, çok daha güçlü ve test edilebilir form yapısıdır. Angular 22 ile birlikte reaktif formların veri tipleri çok
daha güvenli hale gelmiştir. [3, 4]
import { Component, inject } from '@angular/core';
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-kayit-formu',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="profilFormu" (ngSubmit)="gonder()">
<input
formControlName="kullaniciAdi">
<button
[disabled]="profilFormu.invalid">Kaydet</button>
</form>
`
})
export class KayitFormuComponent {
private fb = inject(FormBuilder);
profilFormu = this.fb.group({
kullaniciAdi: ['', [Validators.required, Validators.minLength(3)]]
});
gonder() {
console.log(this.profilFormu.value);
}
}
BÖLÜM 12:
Görsel Bileşenler (Angular Material)
Google'ın hazırladığı hazır tasarım
kütüphanesidir. Sıfırdan buton veya takvim tasarlamak yerine bu hazır yapıları
çağırırız.
BÖLÜM 13:
Bileşen Yaşam Döngüsü (Component Lifecycle Hooks)
Bir bileşen ekrana ilk çizildiği andan, ekrandan
silindiği ana kadar belirli aşamalardan geçer. Bu aşamalarda kod çalıştırmamızı
sağlayan özel metotlar vardır.
|
Metot Adı |
Ne Zaman
Çalışır? |
En Sık
Kullanım Amacı |
|
ngOnChanges |
Dışarıdan
gelen bir girdi (@Input) değiştiğinde. |
Girdi
değişikliklerini takip etmek. |
|
ngOnInit |
Bileşen ilk
kez ayağa kalkıp ekrana çizilmeye hazırlandığında. |
Sunucudan
ilk verileri çekmek. |
|
ngAfterViewInit |
Bileşenin
HTML görüntüsü ve alt elemanları tamamen yüklendiğinde. |
Doğrudan
HTML elementlerine müdahale etmek. |
|
ngOnDestroy |
Kullanıcı
başka sayfaya geçtiğinde ve bileşen yok edildiğinde. |
Açık kalan
sayaçları ve bağlantıları kapatıp hafızayı temizlemek. |
BÖLÜM 14:
Kimlik Doğrulama ve Güvenlik (Authentication &
Security)
Uygulamanın kurumsal seviyede korunmasını sağlar.
1. JWT (JSON Web Token)
Nedir?
Kullanıcı giriş yaptığında sunucu ona şifreli bir
kimlik kartı metni (Token) verir. Uygulama bu kartı
tarayıcıda saklar ve sunucuya her veri isteği gönderdiğinde bu kartı göstererek
kimliğini kanıtlar.
2. CanActivate Guard (Sayfa Koruyucuları)
Kullanıcının giriş yapmadan gizli sayfalara (Örn: Yönetici Paneli) girmesini engelleyen yol
barikatlarıdır. Eğer giriş yapılmamışsa kullanıcıyı doğrudan giriş sayfasına
fırlatır.
3. Web Güvenliği (XSS ve XSRF Koruması)
BÖLÜM 15:
Bileşenler Arası Gelişmiş İletişim (Component Communication)
Aynı sayfadaki farklı bileşenlerin birbiriyle
konuşma yöntemleridir.
1. Parent to Child (Anne-Babadan Çocuğa) - Input Binding
Bir üst bileşenin altındaki bileşene veri
göndermesidir.
// alt.component.ts
@Input() gelenMesaj: string = '';
<!-- ana.component.html -->
<app-alt [gelenMesaj]="'Seni
seviyorum evlat'"></app-alt>
2. Child to Parent (Çocuktan Anne-Babaya) - Output & Custom Events
Alt bileşenin yukarıya sinyal veya olay
fırlatmasıdır.
// alt.component.ts
@Output() tiklamaSinyali = new EventEmitter<void>();
butonaBasildi() {
this.tiklamaSinyali.emit();
}
3. RxJS Reaktif Konuşma
(BehaviorSubject)
Birbirinden
tamamen bağımsız uzak bileşenlerin, ortak bir yayın kanalı üzerinden
haberleşmesidir. Kanala yeni bir veri eklendiğinde, o kanalı dinleyen tüm
bileşenler anında güncellenir.
Gerekli tüm konuları sırasıyla, adım adım
çalışan, birbirine entegre ve tamamen bağımsız çalıştırabileceğiniz 15
farklı komple mini proje halinde tasarladım.
Dosyadaki tüm başlıkları, Angular
22'nin en güncel standartları olan Standalone
Bileşenler, inject() fonksiyonu ve yeni kontrol blokları (@if, @for) ile
hazırladım.
PROJE 1:
Giriş, Mimari ve Klasör Yapısı (Introduction & App Structure)
Amaç: Angular uygulamasının
ana giriş noktalarını, kök bileşeni (AppComponent) ve bootstrap sürecini simüle etmek.
// src/app/app.config.ts
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true })
]
};
// src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<header class="app-header">
<h1>Kurumsal
Mimari Başlangıç Projesi</h1>
</header>
<main class="app-content">
<p>Angular 22 uygulaması başarıyla bootstrap
edildi ve klasör yapısı doğrulandı.</p>
</main>
`,
styles: [`
.app-header { background:
#1976d2; color: white; padding: 15px; text-align: center; }
.app-content { padding: 20px; font-family:
sans-serif; }
`]
})
export class AppComponent {}
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
PROJE 2: TypeScript Esasları (TypeScript
Essentials)
Amaç: Güvenli veri tipleri, Type Aliases, Classes ve Access Modifiers içeren bir
veri modeli simülasyonu çalıştırmak.
// src/app/typescript-demo.component.ts
import { Component, OnInit } from '@angular/core';
// 1. Type
Alias Tanımlaması
type KullaniciTipi = 'Admin' | 'Personel' | 'Ziyaretci';
interface IKullanici {
id: number;
isim: string;
rol: KullaniciTipi;
}
// 2. OOP Sınıf Yapısı
class SirketYoneticisi {
private maasKatsayisi: number = 1.5; // Access Modifier (Private)
constructor(public sirketAdı: string) {} // Otomatik Property Tanımlama
public primHesapla(tabanNot: number): number {
return tabanNot * this.maasKatsayisi;
}
}
@Component({
selector: 'app-ts-demo',
standalone: true,
template: `
<div style="border: 1px solid #ccc; padding: 15px; margin:
10px;">
<h3>TypeScript Nesne ve Sınıf Çıktıları</h3>
<p><strong>Kullanıcı:</strong>
{{ aktifKullanici.isim }} (Rol: {{ aktifKullanici.rol }})</p>
<p><strong>Şirket:</strong>
{{ sirket.sirketAdı }}</p>
<p><strong>Yönetici Primi:</strong>
{{ hesaplananPrim }} TL</p>
</div>
`
})
export class TypescriptDemoComponent implements OnInit {
// Tipli
değişken tanımlamaları
aktifKullanici!: IKullanici;
sirket = new SirketYoneticisi('Teknoloji A.Ş.');
hesaplananPrim: number = 0;
ngOnInit() {
// Nesne
oluşturma ve Destructuring
const hamVeri = { id: 101, isim: 'Can Yılmaz', rol: 'Admin' as KullaniciTipi, sehir: 'Isparta' };
const { id, isim, rol } = hamVeri; // Destructuring
this.aktifKullanici = { id, isim, rol };
this.hesaplananPrim = this.sirket.primHesapla(5000);
}
}
PROJE 3: İlk
Uygulama ve Temel Yönlendirme (First App & Basic
Routing)
Amaç: Sayfa yenilenmeden çalışan (Single Page App) iki farklı
sayfa arası geçiş mekanizması.
// src/app/pages.components.ts
import { Component } from '@angular/core';
@Component({
standalone: true,
template: `<h2>Ana Sayfaya Hoş Geldiniz</h2><p>Burası giriş
ekranıdır.</p>`
})
export class HomePagesComponent {}
@Component({
standalone: true,
template: `<h2>Hakkımızda</h2><p>Kurumsal Angular
22 çözümleri üretiyoruz.</p>`
})
export class AboutPagesComponent {}
// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { HomePagesComponent, AboutPagesComponent } from './pages.components';
export const routes: Routes = [
{ path: '', component: HomePagesComponent },
{ path: 'hakkimizda', component: AboutPagesComponent }
];
// src/app/app.component.ts
(Yönlendirici Ana Bileşen)
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink],
template: `
<nav style="padding: 10px;
background: #eee;">
<a routerLink="/" style="margin-right: 10px;">Ana Sayfa</a>
<a routerLink="/hakkimizda">Hakkımızda</a>
</nav>
<hr>
<router-outlet></router-outlet>
`
})
export class AppComponent {}
PROJE 4: Veri
Bağlama ve Yeni Kontrol Blokları (Data Bindings &
@if/@for)
Amaç: Tek yönlü, çift yönlü veri bağlama, olay
yakalama ve Angular 22 şart/döngü bloklarının
kullanımı.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-binding-demo',
standalone: true,
imports: [FormsModule],
template: `
<div style="padding: 20px;">
<!-- Property Binding & Interpolation -->
<h3 [style.color]="baslikRengi">Dinamik
Başlık: {{ uygulamaAdi }}</h3>
<!-- Two-Way Binding -->
<input
[(ngModel)]="kullaniciGirdisi"
placeholder="Bir şeyler yazın...">
<p>Anlık
Yazılan: {{ kullaniciGirdisi }}</p>
<!-- Event Binding -->
<button
(click)="girdiyiTemizle()">Temizle</button>
<hr>
<!-- Yeni @if
Yapısı -->
@if (kullaniciGirdisi.length >
5) {
<div style="color: green;">Güvenli ve uzun bir metin
yazdınız!</div>
} @else {
<div style="color: red;">Metin çok kısa (En az 6
karakter).</div>
}
<hr>
<!-- Yeni
@for Yapısı -->
<h4>Sistem
Günlükleri</h4>
<ul>
@for (log of loglar; track log.id; let i = $index) {
<li>{{ i + 1 }} - {{ log.mesaj
}}</li>
} @empty {
<li>Henüz hiçbir işlem kaydı yok.</li>
}
</ul>
</div>
`
})
export class BindingDemoComponent {
uygulamaAdi: string = 'Veri Kontrol
Paneli';
baslikRengi: string = '#2c3e50';
kullaniciGirdisi: string = '';
loglar = [
{ id: 1, mesaj: 'Sistem
başlatıldı.' },
{ id: 2, mesaj: 'Oturum açma
isteği gönderildi.' }
];
girdiyiTemizle() {
this.kullaniciGirdisi = '';
}
}
PROJE 5: Görev
Yöneticisi Laboratuvarı (Task Manager Lab)
Amaç: Tamamen bellek üzerinde çalışan, ekleme ve durum
değiştirme özellikli görev takip kartı.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
interface IGorev {
id: number;
baslik: string;
tamamlandi: boolean;
}
@Component({
selector: 'app-task-manager',
standalone: true,
imports: [FormsModule],
template: `
<div style="max-width: 400px; margin: 20px auto; font-family: Arial;">
<h2>Görev
Yöneticisi (Lab v1)</h2>
<div style="margin-bottom:
15px;">
<input [(ngModel)]="yeniGorevBaslik" placeholder="Yeni
görev yaz..." style="padding:
5px; width: 70%;">
<button (click)="gorevEkle()" style="padding: 5px 10px;">Ekle</button>
</div>
@for (item of gorevListesi; track item.id) {
<div style="padding: 10px; border-bottom: 1px solid #eee; display: flex; align-items: center;">
<input type="checkbox" [(ngModel)]="item.tamamlandi" style="margin-right: 10px;">
<span [style.text-decoration]="item.tamamlandi
? 'line-through' : 'none'">
{{ item.baslik }}
</span>
</div>
} @empty {
<p>Listeniz
boş! Harika, tüm işler bitti.</p>
}
</div>
`
})
export class TaskManagerComponent {
yeniGorevBaslik: string = '';
gorevListesi: IGorev[] = [
{ id: 1, baslik: 'Angular 22 Kursu Dinle', tamamlandi: false },
{ id: 2, baslik: 'Kod Pratiği Yap', tamamlandi: true }
];
gorevEkle() {
if (this.yeniGorevBaslik.trim() !== '') {
this.gorevListesi.push({
id: Date.now(),
baslik: this.yeniGorevBaslik,
tamamlandi: false
});
this.yeniGorevBaslik = ''; // Temizle
}
}
}
PROJE 6: Hata
Ayıklama Alıştırması (Debugging Workspace)
Amaç: Konsola log bırakma ve çalışma zamanı hatalarını
inceleme düzeneği kurmak.
import { Component } from '@angular/core';
@Component({
selector: 'app-debug-demo',
standalone: true,
template: `
<div style="padding: 20px; background: #fff3cd; border:
1px solid #ffeeba;">
<h3>Hata
Ayıklama Deney Alanı</h3>
<button
(click)="guvenliIslem()">Güvenli
Konsol İncelemesi</button>
<button
(click)="kritikHataFirlat()"
style="background: red;
color: white;">Hata
Tetikle</button>
</div>
`
})
export class DebugDemoComponent {
guvenliIslem() {
console.log('%c[DEBUG] İşlem adımı tetiklendi, veriler kontrol ediliyor...', 'color: blue; font-weight: bold;');
// Chrome DevTools F12 -> Sources kısmında izlemek için debugger
satırı:
debugger;
}
kritikHataFirlat() {
console.error('[HATA] Sunucu bağlantısı simüle edilerek kesildi!');
throw new Error('Uygulama çalışma zamanı yapay hatası.');
}
}
PROJE 7:
Servisler ve Bağımlılık Enjeksiyonu (Services & inject())
Amaç: İş mantığını bileşenden ayırıp modern inject yapısıyla merkezi servisten tüketmek.
// src/app/log.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class LogService {
private logGecmisi: string[] = [];
sistemeYaz(mesaj: string): void {
const zamanliMesaj = `[${new Date().toLocaleTimeString()}] ${mesaj}`;
this.logGecmisi.push(zamanliMesaj);
console.log(zamanliMesaj);
}
tumLoglariGetir(): string[] {
return this.logGecmisi;
}
}
// src/app/servis-test.component.ts
import { Component, inject, OnInit } from '@angular/core';
import { LogService } from './log.service';
@Component({
selector: 'app-service-demo',
standalone: true,
template: `
<div style="padding: 15px; background: #e2e3e5;">
<h3>Merkezi
Servis ve Bağımlılık Enjeksiyonu</h3>
<button
(click)="islemYap()">Yeni
Log Üret</button>
<h4>Aktivite
Geçmişi:</h4>
@for (l of aktifLoglar;
track $index) {
<p style="font-family: monospace; font-size: 12px; margin:
3px 0;">{{ l }}</p>
}
</div>
`
})
export class ServisTestComponent implements OnInit {
// Angular 22 Constructor yerine inject()
kullanım standardı
private logger = inject(LogService);
aktifLoglar: string[] = [];
ngOnInit() {
this.logger.sistemeYaz('Bileşen başarıyla initialize
edildi.');
this.aktifLoglar = this.logger.tumLoglariGetir();
}
islemYap() {
this.logger.sistemeYaz('Kullanıcı buton aksiyonunu tetikledi.');
this.aktifLoglar = this.logger.tumLoglariGetir();
}
}
PROJE 8: Özel
Boru Filtresi Yapımı (Custom Pipes)
Amaç: Ham veriyi ekrana basarken anlık manipüle eden
saf ve saf olmayan filtre yapısı kurmak.
// src/app/maske.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'krediKartiMaske',
standalone: true
})
export class KrediKartiMaskePipe implements PipeTransform {
transform(value: string): string {
if (!value || value.length
< 16) return value;
// İlk 4
haneyi göster, kalanları yıldızla, son 4 haneyi göster
return value.substring(0, 4) + ' **** **** ' + value.substring(12);
}
}
// src/app/pipe-demo.component.ts
import { Component } from '@angular/core';
import { KrediKartiMaskePipe } from './maske.pipe';
import { CurrencyPipe, DatePipe } from '@angular/common';
@Component({
selector: 'app-pipe-demo',
standalone: true,
imports: [KrediKartiMaskePipe, CurrencyPipe,
DatePipe],
template: `
<div style="padding: 15px; border: 1px dashed purple;">
<h3>Veri
Biçimlendirme (Pipes)</h3>
<p><strong>Kart Numarası (Maskeli):</strong>
{{ kartNo | krediKartiMaske
}}</p>
<p><strong>Bakiye Borç:</strong>
{{ bakiye | currency:'TRY':'symbol':'1.2-2' }}</p>
<p><strong>Son Güncelleme Zamanı:</strong>
{{ bugun | date:'dd/MM/yyyy HH:mm'
}}</p>
</div>
`
})
export class PipeDemoComponent {
kartNo: string = '4355123456789012';
bakiye: number = 14500.75;
bugun: Date = new Date();
}
PROJE 9: HTTP
İletişimi Simülasyonu (HttpClient API Mock)
Amaç: HttpClient kullanarak
dış dünyadan veri çekme ve asenkron veri akış yönetimi.
// src/app/api.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface IPost {
id: number;
title: string;
body: string;
}
@Injectable({ providedIn: 'root' })
export class ApiService {
private http = inject(HttpClient);
private endpoint = 'https://typicode.com';
getYazilar(): Observable<IPost[]>
{
return this.http.get<IPost[]>(this.endpoint);
}
}
// src/app/http-demo.component.ts
import { Component, inject, OnInit } from '@angular/core';
import { ApiService, IPost } from './api.service';
@Component({
selector: 'app-http-demo',
standalone: true,
template: `
<div style="padding: 20px;">
<h3>REST
API Veri Akışı (HttpClient)</h3>
@if (yukleniyor)
{
<p>Uzak
sunucudan veriler indiriliyor...</p>
}
<div style="max-height: 200px; overflow-y: scroll; border: 1px solid #aaa; padding: 10px;">
@for (post of veriListesi;
track post.id) {
<div style="background: #f9f9f9; margin-bottom:
5px; padding: 5px;">
<strong>{{ post.title
}}</strong>
<p style="font-size:12px; margin:2px 0;">{{ post.body }}</p>
</div>
}
</div>
</div>
`
})
export class HttpDemoComponent implements OnInit {
private api = inject(ApiService);
veriListesi: IPost[] = [];
yukleniyor: boolean = true;
ngOnInit() {
this.api.getYazilar().subscribe({
next: (res) => {
this.veriListesi = res.slice(0, 5); // İlk 5 veriyi alalım
this.yukleniyor = false;
},
error: (err) => {
console.error('Hata oluştu:', err);
this.yukleniyor = false;
}
});
}
}
PROJE 10:
Dinamik Veri Grid Yönetimi (Data Grid - Debounce & Sort)
Amaç: Girdi esnasında sistemi yormayan (Debounce) arama motorlu ve filtreli veri tablosu grid
altyapısı.
import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { FormsModule } from '@angular/forms';
interface ICalisan { id: number; isim: string; departman: string; }
@Component({
selector: 'app-data-grid',
standalone: true,
imports: [FormsModule],
template: `
<div style="padding:20px;">
<h3>Gelişmiş
Çalışan Grid Tablosu</h3>
<input
[(ngModel)]="aramaKriteri"
(input)="aramaTetiklendi()"
placeholder="Hızlı ara (Debounce
aktif)..." style="padding:6px;
margin-bottom:10px; width:250px;">
<table
border="1" cellpadding="5"
style="border-collapse:
collapse; width:100%;">
<thead style="background:#eee;">
<tr>
<th>ID</th>
<th (click)="sirala()" style="cursor:pointer; color:blue;">İsim
(Sırala ▲▼)</th>
<th>Departman</th>
</tr>
</thead>
<tbody>
@for (c of filtrelenmişListe;
track c.id) {
<tr>
<td>{{ c.id }}</td>
<td>{{ c.isim }}</td>
<td>{{ c.departman
}}</td>
</tr>
}
</tbody>
</table>
</div>
`
})
export class DataGridComponent implements OnInit {
aramaKriteri: string = '';
private aramaAkisi = new Subject<string>();
hamListe: ICalisan[] = [
{ id: 1, isim: 'Ahmet Yılmaz', departman: 'Ar-Ge' },
{ id: 2, isim: 'Zeynep Kaya', departman: 'İK' },
{ id: 3, isim: 'Can Demir', departman: 'Finans' }
];
filtrelenmişListe: ICalisan[] = [];
duzSiralama: boolean = true;
ngOnInit() {
this.filtrelenmişListe = [...this.hamListe];
// Debounce mekanizması kurulumu
this.aramaAkisi.pipe(
debounceTime(400), // Kullanıcı
yazmayı bıraktıktan sonra 400ms bekle
distinctUntilChanged() // Sadece veri gerçekten değiştiyse
tetikle
).subscribe(metin
=> {
this.filtrelenmişListe = this.hamListe.filter(c =>
c.isim.toLowerCase().includes(metin.toLowerCase())
);
});
}
aramaTetiklendi() {
this.aramaAkisi.next(this.aramaKriteri);
}
sirala() {
this.duzSiralama = !this.duzSiralama;
this.filtrelenmişListe.sort((a, b) =>
this.duzSiralama ? a.isim.localeCompare(b.isim) : b.isim.localeCompare(a.isim)
);
}
}
PROJE 11:
Şablon Odaklı Form Yapısı (Template Driven Forms)
Amaç: HTML attributes
üzerinden hızlı validasyonlu basit form tasarımı ve state kontrolü.
import { Component } from '@angular/core';
import { FormsModule, NgForm } from '@angular/forms';
@Component({
selector: 'app-template-form',
standalone: true,
imports: [FormsModule],
template: `
<div style="max-width:350px;
padding:20px; border:1px solid #ccc;
margin:10px;">
<h4>Giriş
Yap (Template Driven)</h4>
<form
#f="ngForm" (ngSubmit)="formuGonder(f)">
<div style="margin-bottom:10px;">
<label>E-Posta:
</label><br>
<input type="email" name="email"
ngModel required email #emailModel="ngForm">
@if (emailModel.invalid
&& emailModel.touched) {
<br><small style="color:red;">Geçerli
e-posta zorunludur!</small>
}
</div>
<div style="margin-bottom:10px;">
<label>Şifre: </label><br>
<input type="password" name="sifre"
ngModel required minlength="4" #sifreModel="ngForm">
@if (sifreModel.invalid
&& sifreModel.touched) {
<br><small style="color:red;">Şifre
en az 4 hane olmalı!</small>
}
</div>
<button type="submit" [disabled]="f.invalid">Giriş Yap</button>
</form>
</div>
`
})
export class TemplateFormComponent {
formuGonder(formNesnesi: NgForm) {
if (formNesnesi.valid) {
console.log('Form Başarıyla Gönderildi:', formNesnesi.value);
}
}
}
PROJE 12:
Reaktif Form Teknolojisi (Reactive Forms)
Amaç: TypeScript kod
blokları üzerinden tam denetimli dinamik form takibi ve doğrulamalar.
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'app-reactive-form',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<div style="max-width:350px;
padding:20px; border:1px solid blue;
margin:10px;">
<h4>Ürün
Ekleme (Reactive Form)</h4>
<form [formGroup]="urunFormu"
(ngSubmit)="kaydet()">
<div style="margin-bottom:10px;">
<label>Ürün Adı:</label><br>
<input formControlName="urunAdi">
@if (urunFormu.get('urunAdi')?.invalid && urunFormu.get('urunAdi')?.touched) {
<br><small style="color:red;">Ürün
adı en az 3 karakter olmalı!</small>
}
</div>
<div style="margin-bottom:10px;">
<label>Stok Adedi:</label><br>
<input type="number" formControlName="stok">
</div>
<button type="submit" [disabled]="urunFormu.invalid">Formu Kaydet</button>
</form>
</div>
`
})
export class ReactiveFormComponent {
private fb = inject(FormBuilder);
urunFormu = this.fb.group({
urunAdi: ['', [Validators.required, Validators.minLength(3)]],
stok: [10, [Validators.required, Validators.min(1)]]
});
kaydet() {
if (this.urunFormu.valid) {
console.log('Reaktif Form Verisi Çıktısı:', this.urunFormu.value);
}
}
}
PROJE 13:
Görsel Arayüz Elemanları Örneği (Angular Material UI Custom Layout)
Amaç: Angular Material mimarisine uygun şık kart ve buton yapılarının
işlevsel simülasyonu.
import { Component } from '@angular/core';
@Component({
selector: 'app-material-mock',
standalone: true,
template: `
<div style="padding: 20px; font-family: Roboto, sans-serif;">
<!-- MatToolbar Simülasyonu -->
<div style="background:#673ab7; color:white;
padding:15px; font-weight:bold; border-radius:4px 4px
0 0;">
Material
Dashboard Panel
</div>
<!-- MatCard Simülasyonu -->
<div style="box-shadow: 0 4px 8px
rgba(0,0,0,0.1); border:1px solid
#e0e0e0; padding:20px; background:white;
margin-top:15px; border-radius:4px;">
<h3 style="margin-top:0;">MatCard
Başlığı</h3>
<p style="color:#666;">Sistem
analitiği ve raporlama grafikleri aşağıda listelenmiştir.</p>
<hr style="border:0;
border-top:1px solid #eee;">
<!-- MatButton Örnekleri -->
<button style="background:#ff4081;
color:white; border:none;
padding:8px 16px; border-radius:4px; font-weight:bold;
cursor:pointer; margin-right:10px;">
ONAYLA (RAISED)
</button>
<button style="background:transparent; color:#673ab7;
border:1px solid #673ab7; padding:8px 16px;
border-radius:4px; cursor:pointer;">
İPTAL (OUTLINED)
</button>
</div>
</div>
`
})
export class MaterialMockComponent {}
PROJE 14:
Bileşen Yaşam Döngüsü İzleme İstasyonu (Component Lifecycle
Hooks)
Amaç: Bir bileşenin doğuşundan ölümüne kadarki tüm
evrelerini (Init, Destroy) konsolda
yakalamak.
// src/app/lifecycle-child.component.ts
import { Component, OnInit, OnDestroy, Input, OnChanges, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-lifecycle-child',
standalone: true,
template: `<div style="background:#d4edda;
padding:10px;">Sayaç Değeri: {{ sayiGirdisi
}}</div>`
})
export class LifecycleChildComponent implements OnInit, OnDestroy,
OnChanges {
@Input() sayiGirdisi: number = 0;
ngOnChanges(changes: SimpleChanges) {
console.log('[LIFECYCLE] ngOnChanges tetiklendi. Eski/Yeni
değer:', changes);
}
ngOnInit() {
console.log('[LIFECYCLE] child-component doğdu (ngOnInit).');
}
ngOnDestroy() {
console.log('[LIFECYCLE] child-component yok edildi (ngOnDestroy temizliği).');
}
}
// src/app/lifecycle-parent.component.ts
import { Component } from '@angular/core';
import { LifecycleChildComponent } from './lifecycle-child.component';
@Component({
selector: 'app-lifecycle-parent',
standalone: true,
imports: [LifecycleChildComponent],
template: `
<div style="padding:20px;
border:1px solid green;">
<h3>Yaşam
Döngüsü Denetleyicisi (F12 Konsolunu İzleyin)</h3>
<button
(click)="artir()">Girdiyi
Değiştir (ngOnChanges Tetikle)</button>
<button
(click)="Kapat()" style="background:orange;">Alt Bileşeni Kapat (ngOnDestroy Tetikle)</button>
<hr>
@if (bilesenAktif)
{
<app-lifecycle-child [sayiGirdisi]="sayac"></app-lifecycle-child>
}
</div>
`
})
export class LifecycleParentComponent {
sayac: number = 0;
bilesenAktif: boolean = true;
artir() { this.sayac++; }
Kapat() { this.bilesenAktif = !this.bilesenAktif; }
}
PROJE 15:
Güvenlik, Kimlik Doğrulama ve CanActivate Guard (Security & Guards)
Amaç: Yetkisiz kullanıcıların yönetim sayfalarına
erişmesini engelleyen router koruma mekanizması
kurmak.
// src/app/auth.service.ts
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class AuthService {
private girisYapildiMi: boolean = false;
oturumAc() { this.girisYapildiMi = true; }
oturumKapat() { this.girisYapildiMi = false; }
isLoggedIn(): boolean {
return this.girisYapildiMi;
}
}
// src/app/auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const adminGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isLoggedIn()) {
return true; // Giriş izni ver
} else {
alert('Erişim Engellendi! Önce oturum
açmalısınız.');
router.navigate(['/giris']);
return false; // Geçişi blokla
}
};
// src/app/auth-demo.component.ts
import { Component, inject } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'app-auth-demo',
standalone: true,
template: `
<div style="padding:20px;
background:#f8d7da; border:1px solid
#f5c6cb;">
<h3>Güvenlik
Duvarı ve JWT Simülasyonu</h3>
<p><strong>Mevcut Durum:</strong>
{{ auth.isLoggedIn() ? 'Oturum Açık'
: 'Ziyaretçi' }}</p>
<button
(click)="auth.oturumAc()">Sisteme
Giriş Yap (Token Al)</button>
<button
(click)="auth.oturumKapat()">Güvenli
Çıkış Yap</button>
</div>
`
})
export class AuthDemoComponent {
public auth = inject(AuthService);
}
Bu 15 farklı mini projeyi bilgisayarınızda tek
bir çatı altında birleştirmek, aralarında sorunsuz geçiş yapmak ve projeyi
ayağa kaldırmak için gereken tüm entegrasyon adımlarını ve ana kontrol panelini
(Dashboard) hazırladım.
Aşağıdaki adımları sırasıyla uygulayarak tüm
sistemi tek seferde çalıştırabilirsiniz.
BÖLÜM 1:
Uygulamayı Kurma ve Çalıştırma Adımları (CLI Komutları)
Bilgisayarınızda boş bir klasör açın, komut
satırını (Terminal / CMD) başlatın ve şu komutları sırasıyla yazın:
# 1. En güncel Angular
CLI aracını bilgisayarınıza genel olarak kurun
npm install -g @angular/cli
# 2. "angular-masterclass"
adında yeni ve bağımsız (Standalone) bir proje
oluşturun
ng new angular-masterclass
--routing --style=css --standalone
# 3. Oluşturulan proje klasörünün
içerisine giriş yapın
cd angular-masterclass
# 4. Projenin yerel sunucusunu
başlatın ve tarayıcıda (http://localhost:4200) otomatik açın
ng serve --open
BÖLÜM 2: Proje
Kodlarının Entegrasyonu
Projelerin hatasız derlenmesi için gerekli olan
ortak yönlendirme (Routing) ve konfigürasyon dosyalarını güncelleyelim.
1. Rotaların Tanımlanması
Projenizdeki src/app/app.routes.ts dosyasını açın ve içeriğini aşağıdaki gibi
düzenleyin. Bu sayfa, menüden tıklandığında hangi projenin ekrana geleceğini
belirler.
// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { adminGuard } from './auth.service'; // Proje 15
koruması
export const routes: Routes = [
{
path: 'proje1',
loadComponent: () => import('./app.component').then(m => m.AppComponent)
},
{
path: 'proje2',
loadComponent: () => import('./typescript-demo.component').then(m => m.TypescriptDemoComponent)
},
{
path: 'proje4',
loadComponent: () => import('./binding-demo.component').then(m => m.BindingDemoComponent)
},
{
path: 'proje5',
loadComponent: () => import('./task-manager.component').then(m => m.TaskManagerComponent)
},
{
path: 'proje6',
loadComponent: () => import('./debug-demo.component').then(m => m.DebugDemoComponent)
},
{
path: 'proje7',
loadComponent: () => import('./servis-test.component').then(m => m.ServisTestComponent)
},
{
path: 'proje8',
loadComponent: () => import('./pipe-demo.component').then(m => m.PipeDemoComponent)
},
{
path: 'proje9',
loadComponent: () => import('./http-demo.component').then(m => m.HttpDemoComponent)
},
{
path: 'proje10',
loadComponent: () => import('./data-grid.component').then(m => m.DataGridComponent)
},
{
path: 'proje11',
loadComponent: () => import('./template-form.component').then(m => m.TemplateFormComponent)
},
{
path: 'proje12',
loadComponent: () => import('./reactive-form.component').then(m => m.ReactiveFormComponent)
},
{
path: 'proje13',
loadComponent: () => import('./material-mock.component').then(m => m.MaterialMockComponent)
},
{
path: 'proje14',
loadComponent: () => import('./lifecycle-parent.component').then(m => m.LifecycleParentComponent)
},
{
path: 'proje15',
loadComponent: () => import('./auth-demo.component').then(m => m.AuthDemoComponent)
},
// Proje 15
Güvenlik Duvarı ile korunan Örnek Gizli Yönetim Paneli Sayfası
{
path: 'gizli-panel',
canActivate: [adminGuard],
loadComponent: () => import('./material-mock.component').then(m => m.MaterialMockComponent)
}
];
2. HTTP İletişim Sağlayıcısının Eklenmesi
Proje 9 ve 10'da kullandığımız HttpClient yapısının çalışabilmesi için src/app/app.config.ts dosyanıza provideHttpClient() metodunu eklemeniz gerekir.
// src/app/app.config.ts
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient() // HTTP isteklerini tüm projede
aktif eder
]
};
BÖLÜM 3: Ana
Yönetim Menüsü (Dashboard) HTML ve Mimarisi
Bütün projeleri tek bir ekrandan
yönetebileceğiniz, sol tarafı menü, sağ tarafı ise dinamik içerik alanı olan
profesyonel Dashboard yapısı.
src/app/app.component.ts dosyanızın içeriğini silip aşağıdaki komple
entegre kodla değiştirin:
// src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink, RouterLinkActive],
template: `
<div class="dashboard-container">
<!-- SOL MENÜ
PANELİ -->
<aside class="sidebar">
<div class="sidebar-header">
<h2>Angular 22</h2>
<span class="badge">Masterclass</span>
</div>
<nav class="menu-list">
<a routerLink="/proje1" routerLinkActive="active-menu">📁 1. Giriş & Mimari</a>
<a routerLink="/proje2" routerLinkActive="active-menu">⚙️ 2. TypeScript Yapıları</a>
<a routerLink="/proje4" routerLinkActive="active-menu">🔗 4. Veri Bağlama (Bindings)</a>
<a routerLink="/proje5" routerLinkActive="active-menu">✅ 5. Görev Yöneticisi (Lab)</a>
<a routerLink="/proje6" routerLinkActive="active-menu">🪲 6. Hata Ayıklama Paneli</a>
<a routerLink="/proje7" routerLinkActive="active-menu">🛠️ 7. inject() Servis Yapısı</a>
<a routerLink="/proje8" routerLinkActive="active-menu">💎 8. Özel Filtreler (Pipes)</a>
<a routerLink="/proje9" routerLinkActive="active-menu">🌐 9. HTTP Sunucu İletişimi</a>
<a routerLink="/proje10" routerLinkActive="active-menu">📊 10. Gelişmiş Data Grid</a>
<a routerLink="/proje11" routerLinkActive="active-menu">📝 11. Template Form</a>
<a routerLink="/proje12" routerLinkActive="active-menu">🧬 12. Reactive Form</a>
<a routerLink="/proje13" routerLinkActive="active-menu">🎨 13. Material Tasarımları</a>
<a routerLink="/proje14" routerLinkActive="active-menu">🔄 14. Lifecycle Hooks</a>
<a routerLink="/proje15" routerLinkActive="active-menu">🔐 15. Güvenlik & Token</a>
<a routerLink="/gizli-panel" routerLinkActive="active-menu" class="lock-link">🛑 Korunan Gizli Sayfa</a>
</nav>
</aside>
<!-- SAĞ
DİNAMİK İÇERİK ALANI -->
<main class="main-content">
<header class="top-bar">
<h3>Proje
İzleme ve Canlı Çalıştırma İstasyonu</h3>
<div class="status-indicator">
<span class="dot"></span>
Sistem Aktif (Port: 4200)
</div>
</header>
<!--
Seçilen alt mini proje dinamik olarak buraya yüklenecektir -->
<section class="stage">
<router-outlet></router-outlet>
</section>
</main>
</div>
`,
styles: [`
/* Dashboard Genel Arayüz Tasarımı */
.dashboard-container {
display:
flex;
height:
100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f4f6f9;
margin:
0;
}
/* Sol Menü Tasarımı */
.sidebar {
width:
280px;
background-color: #1e293b;
color:
#cbd5e1;
display:
flex;
flex-direction:
column;
box-shadow:
2px 0 10px rgba(0,0,0,0.1);
}
.sidebar-header {
padding:
20px;
background-color: #0f172a;
text-align:
center;
border-bottom:
1px solid #334155;
}
.sidebar-header h2 {
margin:
0;
color:
#fff;
font-size: 22px;
}
.badge {
font-size: 11px;
background:
#3b82f6;
color:
white;
padding:
2px 8px;
border-radius:
10px;
margin-top:
5px;
display:
inline-block;
}
.menu-list {
display:
flex;
flex-direction:
column;
padding:
15px 0;
overflow-y:
auto;
}
.menu-list a {
color:
#94a3b8;
text-decoration:
none;
padding:
12px 25px;
font-size: 14px;
display:
flex;
align-items:
center;
transition:
all 0.2s ease;
}
.menu-list a:hover {
background-color: #334155;
color:
#fff;
}
/* Tıklanan Aktif Menüyü Boyama */
.active-menu {
background-color: #2563eb !important;
color:
#fff !important;
font-weight: bold;
border-left:
5px solid #60a5fa;
}
.lock-link {
border-top:
1px dashed #475569;
margin-top:
10px;
color:
#f87171 !important;
}
/* Sağ İçerik Alanı */
.main-content {
flex:
1;
display:
flex;
flex-direction:
column;
overflow:
hidden;
}
.top-bar {
background-color: #fff;
height:
60px;
display:
flex;
align-items:
center;
justify-content:
space-between;
padding:
0 30px;
border-bottom:
1px solid #e2e8f0;
}
.top-bar h3 {
margin:
0;
color:
#334155;
font-size: 16px;
}
.status-indicator {
font-size: 13px;
color:
#10b981;
display:
flex;
align-items:
center;
}
.dot {
height:
8px;
width:
8px;
background-color: #10b981;
border-radius:
50%;
display:
inline-block;
margin-right:
8px;
animation:
pulse 1.5s infinite;
}
.stage {
flex:
1;
padding:
30px;
overflow-y:
auto;
}
@keyframes pulse {
0% { transform: scale(0.9); opacity: 0.7; }
50% { transform: scale(1.1); opacity: 1; }
100% { transform: scale(0.9); opacity: 0.7; }
}
`]
})
export class AppComponent {}
BÖLÜM 4: Şimdi
Ne Olacak?
Sistem tamamen hazır hale geldi. ng serve komutu
çalıştıktan sonra tarayıcınızda sol taraftaki menü butonlarına tıkladığınız
anda:
"Öğrenecekleriniz" Bölümünün Açıklaması
Gönderdiğiniz
kursta Öğrenecekleriniz başlığı altındaki maddeler, modern bir ön yüz (frontend) geliştiricisi olmak için ihtiyacınız olan tüm
temel ve ileri düzey Angular yeteneklerini
özetlemektedir:
Konu Tasarımı ve Planlama
Eğitim
içeriğini mantıksal bir sıra ile 7 ana bölüme ayırarak tasarlıyoruz. Her
başlığı sırasıyla teorik altyapısı, pratik kod örnekleri ve gerçekçi uygulama
adımlarıyla ele alacağız.
+-------------------------------------------------------------+
| Modern Angular
Geliştirme Haritası |
+-------------------------------------------------------------+
|
+-----------------------+-----------------------+
|
|
[1. Temel Mimariler] [2. Veri ve
Tepkisellik]
- Bağımsız Bileşenler - Sinyaller (Signals)
- Şablonlar & Bağlamalar - Gelişmiş Veri Akışları
|
|
+-----------------------+-----------------------+
|
+-----------------------+-----------------------+
|
|
[3. Dinamik Yapılar] [4. Dış Dünya ile
İletişim]
- Özel Direktifler - HttpClient
API Kontrolü
- Boru Hatları (Pipes) - İnterseptör
Yapıları
|
|
+-----------------------+-----------------------+
|
+-----------------------+-----------------------+
|
|
[5. Kullanıcı Etkileşimi] [6. Sayfa Yönetimi &
Güvenlik]
- Sinyal Tabanlı Formlar - Akıllı Yönlendirme (Routing)
- Gelişmiş Doğrulamalar - Sayfa Koruyucular (Guards)
|
+-----------------+-----------------+
| |
[7. Mimari Optimizasyon] [8. Test Stratejileri]
- Performans & Kod Bölme - Birim Testleri (Unit
Test)
Tüm Konuların Detaylı Açıklaması, Örnekleri ve Uygulamaları
1. Temel Mimari: Bağımsız Bileşenler (Standalone
Components) ve Şablonlar
Angular uygulamalarının en küçük yapı taşına Bileşen
(Component) adı verilir. Eski Angular
versiyonlarında her bileşeni bir modüle (NgModule) kayıt etmek
zorundaydık. Modern Angular dünyasında ise her
bileşen Bağımsız (Standalone) olarak
tanımlanır. Kendi bağımlılıklarını kendisi ithal eder. [1, 2, 8]
Bileşen
Bağlama Türleri (Data Binding)
Uygulama
Örneği: Ürün Kartı Bileşeni
Bir e-ticaret
sitesi için bağımsız bir ürün kartı bileşeni tasarlayalım. Bu bileşen hem
bilgileri ekrana bassın, hem resmi dinamik alsın, hem
de tıklama olayını yakalasın.
TypeScript Kod Dosyası (urun-kart.component.ts)
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-urun-kart',
standalone: true, // Modüle
ihtiyaç duymadığını belirtir
imports: [CommonModule, FormsModule], // Kullanacağı ek özellikleri buraya ekler
templateUrl: './urun-kart.component.html',
styleUrls: ['./urun-kart.component.css']
})
export class UrunKartComponent {
urunAdi: string = 'Kablosuz
Oyuncu Kulaklığı';
urunResmi: string = 'https://picsum.photos';
stokAdedi: number = 5;
kullaniciNotu: string = '';
stokAzalt() {
if (this.stokAdedi > 0) {
this.stokAdedi--;
}
}
}
HTML Şablon
Dosyası (urun-kart.component.html)
<div class="kart">
<!-- String Interpolation -->
<h2>{{ urunAdi }}</h2>
<!-- Property
Binding -->
<img [src]="urunResmi" alt="Ürün Resmi">
<p>Mevcut
Stok: {{ stokAdedi }}</p>
<!-- Event Binding -->
<button (click)="stokAzalt()" [disabled]="stokAdedi === 0">Satın Al</button>
<!-- Two-Way Binding -->
<div class="not-alani">
<input [(ngModel)]="kullaniciNotu" placeholder="Sipariş
notu yazın...">
<p>Notunuz:
<i>{{
kullaniciNotu }}</i></p>
</div>
</div>
2. Tepkisel Yönetim: Sinyaller (Signals) ve
Gelişmiş Veri Akışı
Sinyaller (Signals), Angular'da verinin
değiştiğini sisteme çok hızlı bildiren akıllı veri kutularıdır. Eski sistemde Angular bir değişkenin değişip değişmediğini anlamak için
bütün sayfayı baştan aşağı kontrol ederdi (Change Detection). Sinyaller sayesinde sadece o verinin
kullanıldığı nokta tespit edilir ve ışık hızında güncellenir. [3, 4]
Uygulama
Örneği: Akıllı Alışveriş Sepeti Hesaplayıcısı
TypeScript Kod Dosyası (sepet.component.ts)
import { Component, signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-sepet',
standalone: true,
template: `
<div>
<h3>Alışveriş
Sepetim</h3>
<p>Ürün
Fiyatı: {{ urunFiyati() }} TL</p>
<p>Adet:
{{ adet() }}</p>
<button
(click)="artir()">+</button>
<button
(click)="azalt()">-</button>
<h4>Toplam
Tutar: {{ toplamTutar() }} TL</h4>
</div>
`
})
export class SepetComponent {
// Sinyal
tanımlamaları
urunFiyati = signal<number>(150);
adet = signal<number>(1);
// Computed sinyal: adet veya urunFiyati
değiştiğinde otomatik hesaplanır
toplamTutar = computed(() => this.urunFiyati() * this.adet());
constructor() {
// Effect kullanımı: Değişimleri izler ve konsola yazar
effect(() => {
console.log(`Sepet güncellendi! Yeni adet: ${this.adet()}, Toplam: ${this.toplamTutar()} TL`);
});
}
artir() {
this.adet.update(mevcut => mevcut + 1);
}
azalt() {
if (this.adet() > 1) {
this.adet.update(mevcut => mevcut - 1);
}
}
}
3. Dinamik Yapılar: Özel Direktifler (Directives)
ve Boru Hatları (Pipes)
Direktifler (Directives)
HTML
etiketlerinin davranışlarını veya görünümlerini değiştiren yapılardır. İki
çeşittir:
Boru Hatları (Pipes)
Ekrandaki
veriyi kullanıcının göreceği formata sokar. Örneğin ham bir tarihi 25/08/2026 şekline veya
bir yazıyı tamamen BÜYÜK HARFE çevirir.
Uygulama
Örneği: Özel Parlama Direktifi ve Para Birimi Filtresi
Hem üzerine
gelindiğinde rengi değiştiren bir direktif yazalım, hem de sayıları Türkçe para
formatına çeviren bir Pipe kullanalım.
Özel Direktif
Kodu (parla.directive.ts)
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appParla]',
standalone: true
})
export class ParlaDirective {
@Input() parlamaRengi: string = 'yellow';
constructor(private el: ElementRef) {}
@HostListener('mouseenter') uzerineGeldi() {
this.el.nativeElement.style.backgroundColor = this.parlamaRengi;
}
@HostListener('mouseleave') uzerindenGitti() {
this.el.nativeElement.style.backgroundColor = null;
}
}
Bileşende
Kullanımı (liste.component.ts)
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ParlaDirective } from './parla.directive';
@Component({
selector: 'app-liste',
standalone: true,
imports: [CommonModule, ParlaDirective],
template: `
<!-- Özel direktif ve Dahili Pipe kullanımı -->
<div appParla parlamaRengi="lightblue"
class="kutu">
Dinamik Değer: {{ hamKazanc | currency:'TRY':'symbol':'1.2-2' }}
</div>
<h3>Kurs Katılımcıları</h3>
<ul>
<!-- Yeni
Nesil @for Yapısı -->
@for (kullanici
of kullanicilar; track
kullanici.id) {
<li>{{ kullanici.isim
}}</li>
} @empty {
<li>Kayıtlı kullanıcı bulunamadı.</li>
}
</ul>
`
})
export class ListeComponent {
hamKazanc: number = 1250.75;
kullanicilar =
[
{ id: 1, isim: 'Ahmet Yılmaz' },
{ id: 2, isim: 'Elif Kaya' }
];
}
4. Dış Dünya ile İletişim: Servisler ve HttpClient
Veri saklama, API'lere bağlanma veya backend
sistemleri ile konuşma görevleri bileşenlerin içinde yapılmaz. Bu işler için Servis
(Service) adı verilen merkezi yapılar kurulur. Bu servisler içinden HttpClient nesnesi kullanılarak uzak sunuculara veri
gönderilir veya alınır. [2, 7]
Uygulama
Örneği: Sunucudan Görev Listesi Çekme Uygulaması
Bir API
sunucusundan veri çeken servis ve bunu ekrana basan bileşeni tasarlayalım.
Veri Servisi
Dosyası (gorek.service.ts)
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface Gorek {
id: number;
title: string;
completed: boolean;
}
@Injectable({
providedIn: 'root' // Uygulamanın her yerinden
erişilebilir yapar
})
export class GorekService {
// Yeni nesil
enjeksiyon yöntemi
private http = inject(HttpClient);
private apiUrl = 'https://typicode.com';
gorevleriGetir(): Observable<Gorek[]>
{
return this.http.get<Gorek[]>(this.apiUrl);
}
}
Bileşen
Dosyası (gorev.component.ts)
import { Component, OnInit, inject, signal } from '@angular/core';
import { GorekService, Gorek } from './gorek.service';
@Component({
selector: 'app-gorev',
standalone: true,
template: `
<h2>Görev Planlayıcı</h2>
@if (yukleniyor()) {
<p>Veriler
sunucudan yükleniyor...</p>
}
<ul>
@for (g of gorevler(); track g.id) {
<li>
<input type="checkbox" [checked]="g.completed">
{{ g.title }}
</li>
}
</ul>
`
})
export class GorevComponent implements OnInit {
private gorevService = inject(GorekService);
gorevler = signal<Gorek[]>([]);
yukleniyor = signal<boolean>(true);
ngOnInit() {
this.gorevService.gorevleriGetir().subscribe({
next: (gelenVeriler) => {
this.gorevler.set(gelenVeriler);
this.yukleniyor.set(false);
},
error: (hata) => {
console.error('Veri çekilirken hata oluştu:', hata);
this.yukleniyor.set(false);
}
});
}
}
5. Kullanıcı Etkileşimi: Sinyal Tabanlı Formlar (Signal
Forms)
Yeni nesil Angular mimarisinde form yönetimleri de Sinyal tabanlı hale
getirilmiştir. form() API'si yardımıyla form
alanlarının doluluk oranları, hata durumları ve değerleri tamamen sinyal
fonksiyonları üzerinden asenkron aboneliklere (Subscription) ihtiyaç duymadan
yönetilir. [3, 5, 9]
Uygulama
Örneği: Güvenli Kayıt Olma Formu
TypeScript Kod Dosyası (kayit-form.component.ts)
import { Component, signal } from '@angular/core';
import { form, required, email, minLength } from '@angular/forms/signals';
@Component({
selector: 'app-kayit-form',
standalone: true,
template: `
<form (submit)="kaydet()">
<div>
<label>E-posta:</label>
<input [formField]="userForm.email" type="email">
@if (userForm.email.errors()?.required)
{ <span>Zorunlu alan!</span> }
</div>
<div>
<label>Şifre:</label>
<input [formField]="userForm.password" type="password">
@if (userForm.password.errors()?.minLength)
{ <span>En az 6 karakter!</span> }
</div>
<button
type="submit" [disabled]="userForm.invalid()">Kayıt
Ol</button>
</form>
`
})
export class KayitFormComponent {
// Form modeli
sinyal olarak tanımlanır
formModel = signal({
email: '',
password: ''
});
// Kurallar
şema yardımıyla bağlanır
userForm =
form(this.formModel, (schema) => {
required(schema.email);
email(schema.email);
required(schema.password);
minLength(schema.password, 6);
});
kaydet() {
if (this.userForm.valid()) {
console.log('Sunucuya gönderilen veriler:', this.formModel());
}
}
}
6. Sayfa Yönetimi ve Güvenlik: Akıllı Yönlendirme (Routing) ve Sayfa
Koruyucular (Guards)
Büyük web
uygulamalarında kullanıcıların /anasayfa, /profil veya /ayarlar gibi adreslere gidebilmesi gerekir. Bağımsız
bileşen yapısında yönlendirmeler provideRouter fonksiyonu
ile ana uygulama ayağa kaldırılırken (bootstrap)
sisteme enjekte edilir. Sayfaların yetkisiz kişilerce açılmasını engellemek
için ise Fonksiyonel Guard (Koruyucu) yapıları
kurgulanır. [6, 7, 10, 11, 12]
Uygulama
Örneği: Güvenli Rota Yönetimi
Rota Tanımlama
Dosyası (app.routes.ts)
import { Routes } from '@angular/router';
import { inject } from '@angular/core';
import { Router } from '@angular/router';
// Fake
Giriş Kontrol Fonksiyonu (Guard)
const girisKontrolGuard = () => {
const router = inject(Router);
const girisYaptiMi = localStorage.getItem('isLoggedIn') === 'true';
if (girisYaptiMi) {
return true;
} else {
router.navigate(['/login']);
return false;
}
};
export const routes: Routes = [
{
path: 'login',
loadComponent: () => import('./login.component').then(m => m.LoginComponent)
},
{
path: 'panel',
loadComponent: () => import('./panel.component').then(m => m.PanelComponent),
canActivate: [girisKontrolGuard] // Sayfayı koruma altına alır
},
{ path: '', redirectTo: 'login', pathMatch: 'full' }
];
Ana Uygulama
Başlangıç Noktası (main.ts)
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes), // Rota sistemini aktif eder
provideHttpClient() // HTTP sistemini aktif eder
]
}).catch(err
=> console.error(err));
7. Mimari Optimizasyon: Kod Bölme (Code Splitting) ve Ertelenebilir Görünümler (Deferrable
Views)
Angular web sitelerinin açılış hızını maksimuma çıkarmak
için projedeki tüm kodları tek bir devasa dosya yerine küçük parçalar halinde
derler. Rota geçişlerinde kullanılan loadComponent yapısı
sayesinde ilgili sayfanın kodu sadece kullanıcı o sayfaya tıkladığında
indirilir. [8, 13, 14, 15]
Ayrıca şablon
tasarımlarında @defer blokları kullanılarak, sayfanın alt kısımlarında kalan veya ağır yük
getiren grafik/harita gibi bileşenlerin yüklenmesi, kullanıcının ekranı
kaydırıp o bölgeye gelmesine (viewport) kadar
ertelenebilir.
Uygulama
Örneği: Ağır Bir Yükü Ertelenmiş Yükleme İle Getirme
HTML Şablon
Dosyası (rapor.component.html)
<div class="ust-alan">
<h1>Yönetim Paneli Raporları</h1>
<p>Bu alan
hemen yüklenir ve etkileşime hazır olur.</p>
</div>
<!-- Kullanıcı ekranı kaydırıp bu alana yaklaştığında (on viewport)
bileşen yüklenir -->
@defer (on viewport) {
<app-agir-grafik-bileseni></app-agir-grafik-bileseni>
} @loading {
<p>Grafik
yükleniyor, lütfen bekleyin...</p>
} @placeholder
{
<div class="bos-alan">Grafiği görmek için aşağı kaydırın.</div>
} @error {
<p>Grafik
yüklenirken bir ağ hatası oluştu.</p>
}
8. Test Stratejileri: Birim Testleri (Unit Testing)
Yazdığımız
kodların iş kurallarına tam uyup uymadığını otomatik olarak denetlemek için
birim testleri yazarız. Angular, test ortamı için
hazır şablon araçları barındırır. TestBed nesnesi
kullanılarak sanal bir ortamda bağımsız bileşenimiz ayağa kaldırılır, tıklama
simülasyonları yapılır ve sinyallerin çıktısı doğrulanır.
Uygulama
Örneği: Sinyal Tabanlı Sayacın Test Edilmesi
Test Edilecek
Bileşen Kodu (sayac.component.ts)
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-sayac',
standalone: true,
template: `<button (click)="artir()">{{ deger()
}}</button>`
})
export class SayacComponent {
deger = signal(0);
artir() {
this.deger.update(n => n + 1);
}
}
Test Senaryo
Dosyası (sayac.component.spec.ts)
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { SayacComponent } from './sayac.component';
describe('SayacComponent', () => {
let component: SayacComponent;
let fixture: ComponentFixture<SayacComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SayacComponent] // Bağımsız bileşeni test modülüne ekler
}).compileComponents();
fixture
= TestBed.createComponent(SayacComponent);
component
= fixture.componentInstance;
fixture.detectChanges();
// Değişiklikleri şablona yansıtır
});
it('Başlangıç değeri sıfır olmalıdır', () => {
expect(component.deger()).toBe(0);
});
it('Butona tıklandığında sinyal değeri 1 artmalıdır', () => {
// Buton
elementini bulup tıklama simülasyonu yaparız
const buttonElement: HTMLButtonElement = fixture.nativeElement.querySelector('button');
buttonElement.click();
// Sinyal
değerinin değiştiğini doğrularız
expect(component.deger()).toBe(1);
});
});
1. Eski Sürüm Yapıları: Modül Tabanlı Mimari (Angular
< 16)
Modern Angular projelerinde her bileşen kendi bağımlılığını
yönetirken, eski sürümlerde her yapının bir Modül (NgModule) çatısı altında toplanması zorunluydu.
Uygulamanın çalışabilmesi için en az bir ana modül (AppModule) bulunmalı ve
bileşenler, direktifler ya da boru hatları bu modüle kayıt edilmeliydi.
+-------------------------------------------------------+
| AppModule |
|
|
| [declarations] --> Bileşen, Direktif, Pipe Kaydı |
| [imports] --> Diğer Yardımcı Modüller
(Common) |
| [providers] --> Servis ve Veri
Sağlayıcıları |
| [bootstrap] --> Ana Başlangıç
Bileşeni |
+-------------------------------------------------------+
|
+--------------+--------------+
| |
[TarifBileseni] [ListeBileseni]
Uygulama
Örneği: Modül Tabanlı Tarif ve Alışveriş Listesi Uygulaması
Eski nesil
mimariyi anlamak için bir tarif bileşeni ve bunu tarayıcıya tanıtan modül
dosyasını kurgulayalım.
Modül Dosyası
(app.module.ts)
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { TarifBileseni } from './tarif/tarif.component';
import { DropdownDirektif } from './shared/dropdown.directive';
@NgModule({
declarations: [
AppComponent,
TarifBileseni, // Modüle
bağlı bileşeni buraya kaydediyoruz
DropdownDirektif // Özel
direktif modüldeki tüm bileşenlerin kullanımına açılır
],
imports: [
BrowserModule, // Tarayıcı
özellikleri (ngIf, ngFor
vb. için)
FormsModule // İki yönlü
veri bağlama desteği (ngModel)
],
providers:
[], // Eski nesil servis sağlayıcıları alanı
bootstrap: [AppComponent] // Uygulamanın
ilk çalıştıracağı bileşen
})
export class AppModule { }
Bileşen
Dosyası (tarif.component.ts)
import { Component } from '@angular/core';
@Component({
selector: 'app-tarif',
template: `
<div class="tarif-kart"
appDropdown>
<h3>{{ tarifAdi }}</h3>
<ul>
<li *ngFor="let malzeme of malzemeler">{{ malzeme }}</li>
</ul>
</div>
`
// standalone: true ifadesi ESKİ
yapılarda bulunmaz!
})
export class TarifBileseni {
tarifAdi: string = 'Mercimek
Çorbası';
malzemeler: string[] = ['Mercimek', 'Soğan', 'Havuç', 'Zeytinyağı'];
}
2. NgRx Merkezi Durum Yönetimi (State Management) Mimarisi
Büyük ölçekli
projelerde, bileşenlerin birbirine sürekli veri göndermesi (Input/Output zincirleri) kodun karmaşıklaşmasına neden olur. NgRx, veriyi tek bir merkezi depoda (Store) toplar. Bileşenler bu depodan veri okur veya
depodaki veriyi değiştirmek için emir (Action) yayınlar.
+------------------------------------------------------------+
| NgRx
Döngüsü |
+------------------------------------------------------------+
| ^
v |
[Bileşen (Component)] [Seçici (Selector)]
| |
v (Emir
Yayınlar) |
[Aksiyon (Action)] |
| |
v |
[Yönetici (Reducer)]
----> [Merkezi Depo (Store)] --+
Uygulama
Örneği: NgRx ile Kurs Sepeti Yönetimi
1.
Aksiyonların Tanımlanması (sepet.actions.ts)
import { createAction, props } from '@ngrx/store';
export const kursEkle = createAction(
'[Kurs
Sayfası] Kurs Ekle',
props<{
kursAdi: string }>()
);
export const sepetiTemizle = createAction('[Kurs
Sayfası] Sepeti Temizle');
2. Depo
Yönetici Fonksiyonu (sepet.reducer.ts)
import { createReducer, on } from '@ngrx/store';
import * as SepetAksiyonlari from './sepet.actions';
export interface SepetEyaleti {
secilenKurslar: string[];
}
export const ilkDurum: SepetEyaleti = {
secilenKurslar: []
};
export const sepetReducer = createReducer(
ilkDurum,
// Kurs ekleme
emri geldiğinde yeni durumu üretir
on(SepetAksiyonlari.kursEkle, (mevcutDurum, { kursAdi }) => ({
...mevcutDurum,
secilenKurslar: [...mevcutDurum.secilenKurslar,
kursAdi]
})),
// Sepeti
temizleme emri geldiğinde diziyi boşaltır
on(SepetAksiyonlari.sepetiTemizle, () => ({
secilenKurslar: []
}))
);
3. Bileşen
İçinde Kullanımı (kurs-listesi.component.ts)
import { Component, inject } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { kursEkle } from './sepet.actions';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-kurs-listesi',
standalone: true,
imports: [CommonModule],
template: `
<h3>Mevcut Kurslar</h3>
<button (click)="sepeteEkle('Angular - The Complete
Guide')">Angular Kursunu Ekle</button>
<hr>
<h4>Sepetteki Kurs Sayısı: {{ (sepetKurslari$ | async)?.length }}</h4>
`
})
export class KursListesiComponent {
private store = inject(Store<{ sepet: { secilenKurslar: string[] } }>);
// Depodaki
veriyi canlı olarak dinleyen akış (Observable)
sepetKurslari$: Observable<string[]> = this.store.select(state => state.sepet.secilenKurslar);
sepeteEkle(isim: string) {
// Depoya emir
gönderiliyor
this.store.dispatch(kursEkle({ kursAdi: isim }));
}
}
3. Yeni Nesil Bağımsız Mimari Üzerinden Kapsamlı Uygulama Pratikleri
Angular’ın en güncel standartlarını kullanarak; Sinyaller,
Dinamik Bağımsız Bileşenler ve Yeni Kontrol Akışları (@if, @for) içeren iki
farklı mini proje inşa edelim.
Pratik 1:
Dinamik Bildirim Paneli (Notification Center)
Bu uygulamada,
kullanıcının ekrandan butonlara basarak dinamik uyarı kartları (başarılı, hata,
uyarı) oluşturmasını ve bu kartların sinyaller üzerinden anlık olarak ekrandan
silinmesini sağlayacağız.
Bildirim
Servisi (bildirim.service.ts)
import { Injectable, signal } from '@angular/core';
export interface Bildirim {
id: number;
mesaj: string;
tip: 'success' | 'danger' | 'warning';
}
@Injectable({
providedIn: 'root'
})
export class BildirimService {
// Tüm
bildiriler sinyal dizisinde tutuluyor
bildirimler = signal<Bildirim[]>([]);
private sayac = 0;
bildirimEkle(mesaj: string, tip: 'success' | 'danger' | 'warning') {
const yeni:
Bildirim = { id: ++this.sayac, mesaj, tip };
// Mevcut
dizinin üzerine yeni bildirimi ekleyerek sinyali güncelliyoruz
this.bildirimler.update(liste => [...liste, yeni]);
// 4 saniye
sonra bildirimi otomatik siler
setTimeout(() => this.bildirimSil(yeni.id), 4000);
}
bildirimSil(id: number) {
this.bildirimler.update(liste => liste.filter(b => b.id !== id));
}
}
Bildirim
Ekranı Bileşeni (bildirim-merkezi.component.ts)
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BildirimService } from './bildirim.service';
@Component({
selector: 'app-bildirim-merkezi',
standalone: true,
imports: [CommonModule],
template: `
<div class="yonetim">
<button
(click)="tetikle('İşlem Başarılı!', 'success')">Başarı Ekle</button>
<button
(click)="tetikle('Sistem Hatası!', 'danger')">Hata Ekle</button>
</div>
<div class="bildirim-kutusu">
<!-- Yeni
nesil şablon döngüsü -->
@for (b of servis.bildirimler(); track
b.id) {
<div [ngClass]="['alarm', b.tip]">
<span>{{
b.mesaj }}</span>
<button (click)="servis.bildirimSil(b.id)">x</button>
</div>
}
</div>
`,
styles: [`
.alarm { padding:
10px; margin: 5px; border-radius:
4px; display: flex; justify-content: space-between; }
.success {
background-color: #d4edda; color:
#155724; }
.danger { background-color: #f8d7da; color: #721c24; }
`]
})
export class BildirimMerkeziComponent {
servis = inject(BildirimService);
tetikle(mesaj: string, tip: 'success' | 'danger' | 'warning') {
this.servis.bildirimEkle(mesaj, tip);
}
}
Pratik 2:
Gerçek Zamanlı Arama ve Filtreleme Kartı (Sinyal Tabanlı)
Kullanıcı
arama kutusuna harf girdikçe, sinyal mimarisi sayesinde sunucuya gereksiz istek
atmadan, hafızadaki verileri saniyeler içinde filtreleyen bir akıllı arama
motoru geliştirelim.
Arama Bileşeni
(arama-motoru.component.ts)
import { Component, signal, computed } from '@angular/core';
import { FormsModule } from '@angular/forms';
interface Eleman {
id: number;
baslik: string;
kategori: string;
}
@Component({
selector: 'app-arama-motoru',
standalone: true,
imports: [FormsModule],
template: `
<div class="arama-alani">
<input
[(ngModel)]="aramaMetni"
placeholder="Kurs
veya konu arayın..."
class="arama-girdisi"
>
</div>
<div class="sonuclar">
<h4>Bulunan
Sonuçlar ({{ filtrelenmişElemanlar().length }})</h4>
<ul>
@for (item of
filtrelenmişElemanlar(); track item.id) {
<li>
<strong>{{ item.baslik
}}</strong> - <small>{{
item.kategori }}</small>
</li>
} @empty {
<p>Kriterlere
uygun kurs bulunamadı.</p>
}
</ul>
</div>
`
})
export class AramaMotoruComponent {
//
Kullanıcının yazdığı metni tutan ham sinyal
aramaMetni = signal<string>('');
// Veritabanından gelmiş gibi davranan sabit veri listesi
sinyali
tumVeriler = signal<Eleman[]>([]);
// Computed Sinyal: aramaMetni her
değiştiğinde otomatik olarak listeyi süzebilir
filtrelenmişElemanlar = computed(() => {
const aranan = this.aramaMetni().toLowerCase().trim();
if (!aranan) {
return this.tumVeriler();
}
return this.tumVeriler().filter(item =>
item.baslik.toLowerCase().includes(aranan) ||
item.kategori.toLowerCase().includes(aranan)
);
});
constructor() {
// Sayfa
açıldığında sahte verileri yüklüyoruz
this.tumVeriler.set([
{ id: 1, baslik: 'Angular Geliştirici Kılavuzu', kategori: 'Frontend' },
{ id: 2, baslik: 'RxJS ile Asenkron Programlama', kategori: 'Mimariler' },
{ id: 3, baslik: 'TypeScript Tip Güvenliği Temelleri', kategori: 'Programlama' },
{ id: 4, baslik: 'NgRx ile State
Yönetimi', kategori: 'Frontend' }
]);
}
}