Mert Özen Her Satırda Daha İleri
Frontend Angular — #05

Angular'da Data Binding: Interpolation, Property, Event ve Two-Way Binding

Mert Özen 31 Eki 2026 6 dk 1 görüntülenme
Angular'da Data Binding: Interpolation, Property, Event ve Two-Way Binding

Angular 12'de class ile template arasındaki bütün köprüler: interpolation, property, attribute, class ve style binding, event binding ve $event, template reference variable ve [(ngModel)].

Sürüm notu: Bu yazıdaki kod ve komutlar Angular 12 içindir.

Class ile Template Arasındaki Köprü

Bir component'in iki yarısı var: veriyi tutan class ve o veriyi gösteren template. Data binding, bu iki yarıyı birbirine bağlayan köprünün adı. Köprünün üzerinden trafik iki yönde akar. Class'tan template'e giden veri ekrana yansır, template'ten class'a giden event ise kullanıcının yaptığı şeyi koda bildirir.

Angular'da her yönün kendi sözdizimi var ve bunları bir kez oturttuğunda template okumak çok kolaylaşır. Süslü parantez ve köşeli parantez class'tan template'e, normal parantez template'ten class'a, ikisi bir arada ise iki yöne birden. Bu yazıda hepsini tek bir küçük örnek üzerinden göreceğiz: bir kullanıcı profil kartı.

CLI ile ng g c profile-card komutunu çalıştır ve class'ı şöyle doldur:

export class ProfileCardComponent {
  name = 'Elif Yılmaz';
  role = 'Frontend Developer';
  city = 'İzmir';
  avatarUrl = 'assets/elif.png';
  isOnline = true;
  isSaving = false;
  followers = 128;
  progress = 70;

  follow(): void {
    this.followers++;
  }
}

Interpolation: Süslü Parantezler

Daha önce gördüğümüz interpolation ile başlayalım:

<h3>{{ name }}</h3>
<p>{{ role }} · {{ followers }} takipçi</p>
<p>Durum: {{ isOnline ? 'Çevrim içi' : 'Çevrim dışı' }}</p>
<p>{{ name.toUpperCase() }}</p>

Süslü parantezlerin içi sadece bir property adı olmak zorunda değil; küçük bir TypeScript ifadesi yazabilirsin. Üçüncü satırda ternary operatörüyle duruma göre metin seçtik, dördüncü satırda da bir string metodu çağırdık. Angular ifadenin sonucunu metne çevirip ekrana basar.

Ama sınırsız değil. Atama (=), ++ ya da new gibi şeyler interpolation içinde çalışmaz. Kural şu: template ifadesi bir değer okur, bir şey değiştirmez. Mantık büyümeye başladıysa onu class'a bir metot ya da property olarak taşı.

Property Binding: Köşeli Parantezler

<img [src]="avatarUrl" [alt]="name">
<button [disabled]="isSaving">Kaydet</button>

Köşeli parantez, bir HTML elemanının property'sini class'taki bir değere bağlar. [src]="avatarUrl" ifadesi "img elemanının src property'si her zaman avatarUrl'in değeri olsun" demek. avatarUrl değişirse resim de değişir.

Köşeli parantezin asıl söylediği şey, tırnakların içinin bir TypeScript ifadesi olduğu. alt="name" yazsan alt metni düz "name" kelimesi olur; [alt]="name" yazınca property'nin değeri okunur. Bu yüzden köşeli parantezle sabit bir metin vermek istersen içine ayrıca tek tırnak koyman gerekir: [alt]="'Profil fotoğrafı'".

Fark ettiysen src="{{ avatarUrl }}" da aynı işi görür. Angular bu yazımı arka planda property binding'e çevirir. Metin değerlerinde ikisi de olur, ama disabled gibi boolean bir değeri bağlarken köşeli parantez şart; interpolation her şeyi metne çevirir.

Attribute, Class ve Style Binding

Property binding'in bir inceliği var: HTML attribute'ları ile DOM property'leri aynı şey değil. Attribute HTML'de yazdığın şeydir, property ise tarayıcının o elemandan oluşturduğu JavaScript nesnesinin alanı. Çoğu zaman aynı isimle eşleşirler, ama bazen eşleşmezler. Tablo hücresindeki colspan bunun klasik örneği:

error NG8002: Can't bind to 'colspan' since it isn't a known property of 'td'.

Böyle durumlarda, ya da aria-label gibi karşılığı olan bir property bulunmayan attribute'larda, doğrudan attribute'u bağlarsın:

<td [attr.colspan]="2">Toplam</td>
<button [attr.aria-label]="'Takip et: ' + name">+</button>

Aynı nokta mantığı CSS class'ları ve stiller için de geçerli:

<span class="status" [class.online]="isOnline">●</span>
<div class="bar" [style.width.%]="progress"></div>

[class.online], isOnline true olduğunda elemana online class'ını ekler, false olduğunda kaldırır. Mevcut status class'ına dokunmaz. [style.width.%] ise genişliği yüzde cinsinden ayarlar; sondaki birim px, em ya da % olabilir. Birden fazla class'ı aynı anda yönetmenin daha toplu yollarını ilerleyen yazılarda detaylandıracağız.

Event Binding: Normal Parantezler

Şimdi trafiği ters yöne çevirelim. Kullanıcı bir şey yaptığında class'a haber vermek için:

<button (click)="follow()">Takip et</button>

Parantezin içi dinlenecek event'in adı, tırnakların içi de event gerçekleştiğinde çalışacak kod. Butona her tıklandığında follow() çalışır, followers bir artar ve interpolation sayesinde ekrandaki sayı da kendiliğinden güncellenir. Bu döngü, Angular'ın temel vaadinin ta kendisi: sen veriyi değiştirirsin, ekran gerisini halleder.

Interpolation'ın aksine burada atama yapabilirsin. (click)="isOnline = !isOnline" geçerli bir event ifadesi. Yine de bir satırı geçen mantığı class'ta bir metoda koymak, template'i okunur tutar.

$event ve Strict Mod

Bazen event'in kendisine ihtiyacın olur: hangi tuşa basıldı, input'a ne yazıldı? Angular event nesnesini template'te $event adıyla verir. İnternette sık göreceğin şu satırı deneyelim:

<input (input)="name = $event.target.value">

Angular 12'nin strict modunda bu satır derlenmez:

error TS2339: Property 'value' does not exist on type 'EventTarget'.
error TS2531: Object is possibly 'null'.

Derleyici haklı. $event genel bir Event tipinde ve onun target alanının bir input elemanı olduğunu kimse garanti etmiyor. Çözüm, tip bilgisini class'ta açıkça vermek:

<input (input)="onNameInput($event)">
onNameInput(event: Event): void {
  const input = event.target as HTMLInputElement;
  this.name = input.value;
}

as HTMLInputElement ile derleyiciye "bu bir input, bana güven" diyoruz. Strict mod ilk başta can sıkıcı gelebilir ama tam olarak bu tür "bende çalışıyordu" hatalarını tarayıcıya gitmeden yakalamak için var.

Template Reference Variable: #ref

Çoğu zaman $event ile uğraşmana bile gerek yok. Bir elemana template içinde isim verebilirsin:

<input #cityInput placeholder="Şehir">
<button (click)="setCity(cityInput.value)">Kaydet</button>
setCity(value: string): void {
  this.city = value;
}

#cityInput, input elemanının kendisine template içinden ulaşmanı sağlayan bir etiket. Angular bu değişkenin bir HTMLInputElement olduğunu bildiği için cityInput.value strict modda da sorunsuz derlenir. Değişken sadece o template içinde yaşar, class'tan görünmez.

Bir de kısayol: kullanıcı Enter'a bastığında kaydetmek istersen (keyup.enter)="setCity(cityInput.value)" yazman yeterli. Angular, tuşu senin yerine filtreler.

Two-Way Binding: [(ngModel)]

Form alanlarında genellikle iki yön birden lazımdır. Class'taki değer input'ta görünsün, kullanıcı yazdıkça da class'taki değer güncellensin:

<input [(ngModel)]="name">
<p>Merhaba, {{ name }}</p>

Input'a yazdığın her harf anında paragrafa yansır. Ama bu kodu yazıp kaydettiğinde büyük ihtimalle şu hatayı görürsün:

error NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'.

Daha önce gördüğümüz module mantığı burada devreye giriyor. ngModel bir directive ve FormsModule içinde yaşıyor. Kullanabilmek için AppModule'ün imports listesine eklemen gerekiyor:

import { FormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    AppComponent,
    ProfileCardComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Formların geri kalanını, yani doğrulamayı ve form state'ini ilerleyen yazılarda detaylandıracağız. Şimdilik ngModel'in iki yönlü bağlantı kurduğunu bilmen yeterli.

Banana in a Box: [( )] Aslında Ne?

[()] sözdizimine Angular dünyasında "banana in a box" denir: köşeli kutunun içinde iki muz. Hangisinin içte olduğunu karıştırırsan bu resmi hatırla; muzlar kutunun içinde, yani önce köşeli sonra normal parantez.

Bu yazım sihir değil, iki tanıdık parçanın kısaltması. Yukarıdaki satır birebir şuna eşittir:

<input [ngModel]="name" (ngModelChange)="name = $event">

Köşeli parantezli kısım değeri class'tan input'a taşıyan property binding. Normal parantezli kısım ise input değiştiğinde yeni değeri $event olarak getiren event binding. Burada $event bir DOM event'i değil, doğrudan yeni metin.

Bu kural sadece ngModel'e özel değil. x adında bir input ve xChange adında bir output'u olan her component [(x)] ile kullanılabilir. Kendi component'lerimize input ve output eklemeyi ilerleyen yazılarda detaylandıracağız.

Küçük Bir Deneme

Profil kartına bir "Çevrim içi / Çevrim dışı" butonu ekle. Butona tıklandığında isOnline tersine dönsün, [class.online] ile durum noktasının rengi değişsin, buton metni de interpolation ile duruma göre güncellensin. Ardından bir [(ngModel)] input'u ile role alanını düzenlenebilir yap.

Son olarak progress değerini artıran ve azaltan iki buton koy, [style.width.%] ile çubuğun uzayıp kısaldığını izle. Butonlardan birini progress 100 olduğunda [disabled] ile pasif hâle getir.

Bu denemede bir koşula göre bir class açıp kapattık. Peki bir elemanı koşula göre tamamen ekrandan kaldırmak ya da bir listeyi tek satırla çoğaltmak istersek? Sıradaki konumuz tam olarak bu: Angular'ın hazır directive'leri.