Angular’da Bileşen Etkileşimi: @Input() Dekorasyonu ile Veri Akışı
Angular uygulamaları, küçük, yeniden kullanılabilir ve bağımsız bileşenlerden oluşan bir ağ üzerine kuruludur. Bu bileşenlerin tek başlarına işlevsel olmaları yetmez; aynı zamanda birbirleriyle etkileşime girmeleri, veri alışverişinde bulunmaları ve uygulamanın genel mantığını oluşturmak için işbirliği yapmaları gerekir. Bu etkileşimin temel taşlarından biri, üst bileşenden (parent component) alt bileşene (child component) veri aktarımını sağlayan @Input() dekorasyonudur. Bu makalede, Angular’da @Input() kullanımını, detaylarını, ilgili yaşam döngüsü kancalarını, değişim algılama mekanizmalarını ve en iyi uygulamaları derinlemesine inceleyeceğiz.
Bileşen Etkileşimi Neden Önemlidir?
Modern web uygulamaları genellikle karmaşık kullanıcı arayüzlerine ve iş mantığına sahiptir. Bu karmaşıklığı yönetmek için, uygulamanın farklı bölümlerini soyutlamak ve her birini ayrı, yönetilebilir bir birim olarak ele almak esastır. Angular bileşenleri bu soyutlamayı sağlar. Ancak, bu bağımsız birimler çoğu zaman birbirlerinin verilerine veya durumlarına ihtiyaç duyarlar. Örneğin, bir “ürün listesi” bileşeni, her bir ürünü göstermek için bir “ürün kartı” bileşenini kullanabilir. “Ürün listesi” bileşeni, hangi ürünün gösterileceğini “ürün kartı” bileşenine bildirmelidir. İşte tam bu noktada, bileşenler arası veri akışı devreye girer.
Angular’da bileşenler arası iletişim genellikle iki ana yönde gerçekleşir:
1. Üstten Alta (Parent to Child): Üst bileşen, alt bileşenin davranışını veya içeriğini etkilemek için ona veri gönderir. Bu, @Input() dekorasyonu ile sağlanır.
2. Alttan Üste (Child to Parent): Alt bileşen, üst bileşene bir olayın gerçekleştiğini veya verinin değiştiğini bildirmek ister. Bu, @Output() dekorasyonu ve EventEmitter ile sağlanır.
Bu makalede odak noktamız, üstten alta veri akışını sağlayan @Input() olacaktır.
@Input() Dekorasyonunun Temelleri
@Input() dekorasyonu, bir alt bileşenin üst bileşenden veri alabilmesini sağlayan bir mekanizmadır. Alt bileşende bir özelliği @Input() olarak işaretlediğinizde, bu özellik dışarıdan, yani o alt bileşeni kullanan üst bileşenden değer alabilir hale gelir.
Basit Kullanım
Bir ChildComponent‘in veri adında bir @Input() özelliğine sahip olduğunu varsayalım:
// child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template:
<p>Üst bileşenden gelen veri: {{ veri }}</p>
,
standalone: true
})
export class ChildComponent {
@Input() veri: string = ''; // 'veri' özelliği dışarıdan alınabilir
}
Bu ChildComponent‘i bir ParentComponent içinde kullanmak için:
// parent.component.ts
import { Component } from '@angular/core';
import { ChildComponent } from './child.component'; // ChildComponent'i import et
@Component({
selector: 'app-parent',
template:
<h2>Parent Component</h2>
<app-child [veri]="parentVerisi"></app-child>
<button (click)="degistirVeri()">Veriyi Değiştir</button>
,
standalone: true,
imports: [ChildComponent] // ChildComponent'i kullanmak için import et
})
export class ParentComponent {
parentVerisi: string = 'Merhaba Dünya!';
degistirVeri() {
this.parentVerisi = 'Yeni Mesaj: ' + new Date().toLocaleTimeString();
}
}
Yukarıdaki örnekte:
* ChildComponent içindeki veri özelliği @Input() ile işaretlenmiştir.
* ParentComponent, app-child etiketini kullanarak ChildComponent‘i şablonuna ekler.
* [veri]="parentVerisi" syntax’ı, ParentComponent‘teki parentVerisi değişkeninin değerini ChildComponent‘teki veri @Input() özelliğine bağlar. Köşeli parantezler [], tek yönlü veri bağlamayı (one-way data binding) gösterir ve parentVerisi‘nin değerinin veri‘ye atandığı anlamına gelir.
Farklı Veri Tipleriyle Çalışma
@Input() sadece string değil, her türlü JavaScript veri tipini (sayılar, boolean’lar, nesneler, diziler) alabilir.
// child.component.ts
import { Component, Input } from '@angular/core';
interface Urun {
id: number;
ad: string;
fiyat: number;
}
@Component({
selector: 'app-child',
template:
<div *ngIf="urun">
<h3>Ürün Bilgisi</h3>
<p>ID: {{ urun.id }}</p>
<p>Ad: {{ urun.ad }}</p>
<p>Fiyat: {{ urun.fiyat | currency:'USD':'symbol':'1.2-2' }}</p>
</div>
<div *ngIf="sayilar && sayilar.length > 0">
<h3>Sayılar</h3>
<ul>
<li *ngFor="let s of sayilar">{{ s }}</li>
</ul>
</div>
<p>Aktif Mi: {{ aktifMi ? 'Evet' : 'Hayır' }}</p>
,
standalone: true
})
export class ChildComponent {
@Input() urun?: Urun;
@Input() sayilar?: number[];
@Input() aktifMi: boolean = false;
}
ParentComponent‘ten bu değerleri göndermek:
// parent.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common'; // ngIf ve ngFor için
import { ChildComponent } from './child.component';
interface Urun {
id: number;
ad: string;
fiyat: number;
}
@Component({
selector: 'app-parent',
template:
<h2>Parent Component</h2>
<app-child
[urun]="seciliUrun"
[sayilar]="listeSayilar"
[aktifMi]="isAktif"
></app-child>
<button (click)="degistirUrun()">Ürünü Değiştir</button>
<button (click)="degistirSayilar()">Sayıları Değiştir</button>
<button (click)="toggleAktif()">Aktif Durumu Değiştir</button>
,
standalone: true,
imports: [CommonModule, ChildComponent]
})
export class ParentComponent {
seciliUrun: Urun = { id: 1, ad: 'Laptop', fiyat: 1200 };
listeSayilar: number[] = [10, 20, 30];
isAktif: boolean = true;
degistirUrun() {
this.seciliUrun = { id: 2, ad: 'Mouse', fiyat: 25 };
}
degistirSayilar() {
this.listeSayilar = [40, 50, 60, 70]; // Yeni bir dizi referansı oluşturmak önemli
}
toggleAktif() {
this.isAktif = !this.isAktif;
}
}
Input Alias (Takma Ad) Kullanımı
Bazen, alt bileşendeki @Input() özelliğinin adının, üst bileşenin şablonunda kullanılan adından farklı olmasını isteyebilirsiniz. Bu, isim çakışmalarını önlemek veya daha okunabilir bir şablon oluşturmak için faydalı olabilir. @Input() dekorasyonuna bir string argüman geçirerek takma ad tanımlayabilirsiniz:
// child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template:
<p>Gelen Başlık: {{ baslik }}</p>
,
standalone: true
})
export class ChildComponent {
@Input('disaridanGelenBaslik') baslik: string = ''; // 'disaridanGelenBaslik' takma adını kullanır
}
ParentComponent içinde kullanım:
// parent.component.ts
import { Component } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
template:
<h2>Parent Component</h2>
<app-child [disaridanGelenBaslik]="'Uygulama Başlığı'"></app-child>
,
standalone: true,
imports: [ChildComponent]
})
export class ParentComponent {
// ...
}
Burada, ChildComponent içinde baslik adında bir özellik olmasına rağmen, ParentComponent onu [disaridanGelenBaslik] olarak kullanır.
Inputlara Varsayılan Değerler Atama
Bir @Input() özelliğine varsayılan bir değer atamak oldukça yaygın bir senaryodur. Bu, üst bileşen bir değer sağlamazsa, alt bileşenin yine de anlamlı bir başlangıç değerine sahip olmasını sağlar.
// child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template:
<p>Mesaj: {{ mesaj }}</p>
,
standalone: true
})
export class ChildComponent {
@Input() mesaj: string = 'Varsayılan Mesaj'; // Varsayılan değer atandı
}
Eğer ParentComponent mesaj input’una bir değer atamazsa, ChildComponent “Varsayılan Mesaj” gösterecektir. Eğer atarsa, atanan değer kullanılacaktır.
@Input() ile İlgili Yaşam Döngüsü Kancaları
Angular bileşenlerinin, yaşam döngüleri boyunca belirli anlarda tetiklenen kancaları vardır. @Input() ile etkileşimde olan iki önemli yaşam döngüsü kancası ngOnChanges ve ngOnInit‘tir.
ngOnChanges
ngOnChanges kancası, bileşenin @Input() özellikleri ilk kez ayarlandığında ve daha sonra bu özelliklerden herhangi biri değiştiğinde çağrılır. Bu kanca, değişiklikleri izlemek ve bunlara tepki vermek için ideal bir yerdir.
ngOnChanges metoduna SimpleChanges tipinde bir argüman gelir. Bu obje, değişen her @Input() özelliği için anahtar-değer çiftleri içerir. Her değer, SimpleChange tipinde bir objedir ve previousValue, currentValue ve firstChange (ilk değişiklik mi?) özelliklerini barındırır.
// child.component.ts
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-child',
template:
<p>Gelen Sayı: {{ gelenSayi }}</p>
<p>Gelen Metin: {{ gelenMetin }}</p>
,
standalone: true
})
export class ChildComponent implements OnChanges {
@Input() gelenSayi: number = 0;
@Input() gelenMetin: string = '';
ngOnChanges(changes: SimpleChanges): void {
console.log('ngOnChanges tetiklendi:', changes);
if (changes['gelenSayi']) {
console.log('gelenSayi değişti. Önceki:', changes['gelenSayi'].previousValue, 'Yeni:', changes['gelenSayi'].currentValue);
}
if (changes['gelenMetin'] && changes['gelenMetin'].firstChange) {
console.log('gelenMetin ilk kez ayarlandı.');
}
}
}
ngOnChanges‘in önemli özellikleri:
* Birden Fazla Input: Birden fazla @Input() özelliği değişirse, ngOnChanges tek bir seferde çağrılır ve changes objesi tüm değişen inputları içerir.
* Referans Değişiklikleri: ngOnChanges, sadece input değerlerinin referansı değiştiğinde tetiklenir. Nesneler veya diziler için, eğer objenin içindeki bir özellik değişir ancak objenin referansı aynı kalırsa, ngOnChanges tetiklenmez. Bu, değişim algılama mekanizmasının temel bir özelliğidir ve performans için önemlidir.
ngOnInit
ngOnInit kancası, bileşen ilk kez başlatıldığında, yani tüm @Input() özellikleri ayarlandıktan sonra bir kez çağrılır. Bu, bileşenin başlangıç durumunu ayarlamak veya @Input() değerlerine dayalı ilk hesaplamaları yapmak için uygun bir yerdir.
ngOnInit ve ngOnChanges arasındaki temel fark:
* ngOnChanges: Bileşen oluşturulduğunda ve @Input() değerleri her değiştiğinde çağrılır. changes objesi ile önceki ve yeni değerlere erişim sağlar.
ngOnInit: Bileşen oluşturulduğunda, @Input() değerleri ayarlandıktan sonra* sadece bir kez çağrılır. Bu noktada, tüm input değerleri zaten bileşen özelliklerine atanmıştır.
// child.component.ts
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-child',
template:
<p>Başlangıç Mesajı: {{ baslangicMesaji }}</p>
<p>Gelen Sayı: {{ gelenSayi }}</p>
,
standalone: true
})
export class ChildComponent implements OnChanges, OnInit {
@Input() gelenSayi: number = 0;
baslangicMesaji: string = '';
ngOnChanges(changes: SimpleChanges): void {
if (changes['gelenSayi']) {
console.log('ngOnChanges: gelenSayi değişti:', changes['gelenSayi'].currentValue);
}
}
ngOnInit(): void {
console.log('ngOnInit: Bileşen başlatıldı. gelenSayi:', this.gelenSayi);
this.baslangicMesaji = Bileşen ${this.gelenSayi} değeriyle başlatıldı.;
}
}
Bu örnekte, ngOnInit içinde this.gelenSayi‘ye güvenle erişebiliriz çünkü bu noktada @Input() değerleri zaten atanmış olacaktır.
Değişim Algılama ve @Input()
Angular’ın değişim algılama mekanizması, uygulamanın performansını doğrudan etkiler. @Input() ile veri akışı sırasında değişim algılamanın nasıl çalıştığını anlamak kritik öneme sahiptir.
Angular, varsayılan olarak “check every time” (her zaman kontrol et) stratejisini kullanır. Bu, herhangi bir olay (kullanıcı etkileşimi, HTTP isteği tamamlanması, zamanlayıcı) tetiklendiğinde, Angular’ın tüm bileşen ağacını baştan aşağı kontrol ederek input’larda ve bağlı verilerde bir değişiklik olup olmadığını anlamaya çalıştığı anlamına gelir.
Nesneler ve Dizilerde Referans Değişikliği
Daha önce de belirtildiği gibi, ngOnChanges ve dolayısıyla Angular’ın değişim algılama sistemi, nesneler ve diziler için referans değişikliğini izler. Eğer bir nesnenin veya dizinin içindeki bir özellik değişir ancak nesnenin/dizinin kendisinin bellekteki referansı değişmezse, Angular bunu bir değişiklik olarak algılamaz ve ngOnChanges tetiklenmez.
Örnek:
// parent.component.ts
// ...
export class ParentComponent {
urunBilgisi = { ad: 'Klavye', fiyat: 50 };
degistirUrunFiyat() {
this.urunBilgisi.fiyat = 60; // NESNENİN REFERANSI DEĞİŞMEDİ!
}
degistirUrunReferans() {
this.urunBilgisi = { ...this.urunBilgisi, fiyat: 70 }; // Yeni bir nesne referansı oluşturuldu
}
}
ChildComponent‘e [urun]="urunBilgisi" şeklinde gönderildiğinde:
* degistirUrunFiyat() çağrıldığında, ChildComponent‘teki ngOnChanges tetiklenmez. Angular, urunBilgisi‘nin referansının aynı olduğunu görür.
* degistirUrunReferans() çağrıldığında, ChildComponent‘teki ngOnChanges tetiklenir çünkü urunBilgisi‘ne yeni bir nesne referansı atanmıştır.
Bu nedenle, nesne ve dizi @Input()‘larını güncellerken değişmezlik (immutability) prensibini uygulamak önemlidir. Yeni bir nesne veya dizi oluşturarak (örneğin spread operatörü ... ile), Angular’ın değişikliği algılamasını sağlayabilirsiniz.
OnPush Değişim Algılama Stratejisi
Performansı optimize etmek için, bileşenler ChangeDetectionStrategy.OnPush kullanarak Angular’ın değişim algılama mekanizmasını daha kısıtlı hale getirebilirler. OnPush stratejisiyle bir bileşen, yalnızca aşağıdaki durumlarda değişim algılama döngüsünü tetikler:
1. Bileşenin herhangi bir @Input() özelliğinin referansı değiştiğinde.
2. Bileşenin kendisi veya şablonundaki bir olay (click, submit vb.) tetiklendiğinde.
3. Bileşende bir async pipe tarafından gözlemlenen bir Observable yeni bir değer yaydığında.
4. ChangeDetectorRef servisi kullanılarak manuel olarak değişim algılama tetiklendiğinde (markForCheck(), detectChanges()).
OnPush stratejisini kullanmak, özellikle çok sayıda iç içe bileşeni olan büyük uygulamalarda performansı önemli ölçüde artırabilir. Ancak, @Input() değerlerini güncellerken değişmezlik kurallarına sıkı sıkıya uymayı gerektirir.
// child.component.ts
import { Component, Input, OnChanges, SimpleChanges, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-child',
template:
<p>OnPush ile Gelen Sayı: {{ sayi }}</p>
<p>OnPush ile Gelen Obje Adı: {{ obje.ad }}</p>
,
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush // OnPush stratejisi
})
export class ChildComponent implements OnChanges {
@Input() sayi: number = 0;
@Input() obje: { ad: string } = { ad: '' };
ngOnChanges(changes: SimpleChanges): void {
console.log('OnPush ChildComponent ngOnChanges:', changes);
}
}
ParentComponent‘ten OnPush bileşenine veri gönderirken:
// parent.component.ts
// ...
export class ParentComponent {
onPushSayi: number = 1;
onPushObje: { ad: string } = { ad: 'İlk Obje' };
degistirOnPushSayi() {
this.onPushSayi++; // Primitive değer değişti, OnPush tetikler
}
degistirOnPushObjeIci() {
this.onPushObje.ad = 'Değişen Obje Adı'; // OnPush tetiklemez! Referans aynı.
}
degistirOnPushObjeReferans() {
this.onPushObje = { ...this.onPushObje, ad: 'Yeni Obje Referansı' }; // OnPush tetikler! Yeni referans.
}
}
degistirOnPushObjeIci() metodu çağrıldığında, ChildComponent‘teki ngOnChanges tetiklenmez ve şablon güncellenmez çünkü onPushObje‘nin referansı değişmemiştir. Bu, OnPush stratejisinin temel bir kısıtlamasıdır.
Gelişmiş @Input() Kullanım Senaryoları
Input Setters/Getters
@Input() özelliklerine setter ve getter metodları ekleyerek, input değerlerinin bileşene atanmadan önce veya bileşenden okunurken işlenmesini sağlayabilirsiniz. Bu, değerleri dönüştürmek, doğrulamak veya yan etkileri tetiklemek için harika bir yoldur.
// child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template:
<p>İşlenmiş Başlık: {{ _baslik }}</p>
<p>Doğrulanmış Yaş: {{ _yas }}</p>
,
standalone: true
})
export class ChildComponent {
_baslik: string = '';
_yas: number = 0;
@Input()
set baslik(value: string) {
// Başlığı her zaman büyük harfe dönüştür
this._baslik = value ? value.toUpperCase() : 'BAŞLIK YOK';
}
get baslik(): string {
return this._baslik;
}
@Input()
set yas(value: number) {
// Yaşı doğrula ve pozitif bir sayı olduğundan emin ol
this._yas = value > 0 ? value : 0;
console.log('Yaş input setter tetiklendi:', this._yas);
}
get yas(): number {
return this._yas;
}
}
ParentComponent‘ten kullanım:
// parent.component.ts
import { Component } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
template:
<h2>Parent Component</h2>
<app-child [baslik]="'küçük harfli başlık'" [yas]="-5"></app-child>
<app-child [baslik]="'büyük harfli başlık'" [yas]="30"></app-child>
,
standalone: true,
imports: [ChildComponent]
})
export class ParentComponent {
// ...
}
Bu örnekte, baslik input’u her zaman büyük harfe dönüştürülür ve yas input’u negatif bir değer girilse bile 0 olarak ayarlanır. Setter’lar, ngOnChanges‘ten önce tetiklenir ve bu nedenle ngOnChanges‘te göreceğiniz currentValue zaten setter tarafından işlenmiş değer olacaktır.
Dinamik Inputlar ve ng-template ile İçerik Projeksiyonu
Bazen bir bileşenin sadece veri almakla kalmayıp, aynı zamanda üst bileşenden bir şablon veya HTML parçası alması gerekebilir. Bu, Angular’ın içerik projeksiyonu (content projection) özelliğiyle () veya daha dinamik olarak ng-template‘i @Input() olarak geçirerek yapılabilir.
ng-template‘i @Input() olarak geçirmek, alt bileşenin ne tür bir içerik render edeceğini tam olarak kontrol etmesine olanak tanır ve aynı zamanda üst bileşene bu içeriği özelleştirme esnekliği sunar.
// card.component.ts
import { Component, Input, TemplateRef } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-card',
template:
<div class="card">
<div class="card-header">
<!-- Başlık şablonu varsa onu kullan, yoksa varsayılan başlığı göster -->
<ng-container *ngIf="headerTemplate; else defaultHeader">
<ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: title, data: itemData }"></ng-container>
</ng-container>
<ng-template #defaultHeader>
<h3>{{ title }}</h3>
</ng-template>
</div>
<div class="card-body">
<!-- İçerik şablonu varsa onu kullan, yoksa varsayılan içeriği göster -->
<ng-container *ngIf="bodyTemplate; else defaultBody">
<ng-container *ngTemplateOutlet="bodyTemplate; context: { $implicit: itemData }"></ng-container>
</ng-container>
<ng-template #defaultBody>
<p>Varsayılan Kart İçeriği</p>
<p *ngIf="itemData">Veri: {{ itemData | json }}</p>
</ng-template>
</div>
</div>
,
styles: [
.card { border: 1px solid #ccc; padding: 15px; margin: 10px; border-radius: 5px; }
.card-header { background-color: #f0f0f0; padding: 10px; border-bottom: 1px solid #eee; }
.card-body { padding: 10px; }
],
standalone: true,
imports: [CommonModule]
})
export class CardComponent {
@Input() title: string = 'Kart Başlığı';
@Input() itemData: any; // İçerik şablonuna geçecek veri
@Input() headerTemplate?: TemplateRef<any>; // Başlık için özel şablon
@Input() bodyTemplate?: TemplateRef<any>; // İçerik için özel şablon
}
ParentComponent‘ten bu CardComponent‘i kullanmak:
// parent.component.ts
import { Component } from '@angular/core';
import { CardComponent } from './card.component';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-parent',
template:
<h2>Parent Component - Template Input</h2>
<app-card
[title]="'Ürün Detayı'"
[itemData]="{ name: 'Akıllı Telefon', price: 999 }"
[headerTemplate]="customHeader"
[bodyTemplate]="productBodyTemplate"
></app-card>
<app-card
[title]="'Basit Kart'"
[itemData]="{ message: 'Bu basit bir karttır.' }"
></app-card>
<!-- Özel başlık şablonu -->
<ng-template #customHeader let-title let-data="data">
<h3 style="color: blue;">Özel Başlık: {{ title }} (ID: {{ data?.id || 'N/A' }})</h3>
</ng-template>
<!-- Ürün detayları için özel içerik şablonu -->
<ng-template #productBodyTemplate let-product>
<div style="background-color: #e6ffe6; padding: 5px;">
<p><strong>Ürün Adı:</strong> {{ product.name }}</p>
<p><strong>Fiyat:</strong> {{ product.price | currency:'USD' }}</p>
</div>
</ng-template>
,
standalone: true,
imports: [CommonModule, CardComponent]
})
export class ParentComponent {
// ...
}
Bu örnekte, CardComponent‘e TemplateRef tipinde headerTemplate ve bodyTemplate inputları tanımlanmıştır. ParentComponent, etiketleri ile bu şablonları oluşturur ve CardComponent‘e gönderir. *ngTemplateOutlet direktifi, bu şablonları alt bileşenin içinde render etmek için kullanılır ve context objesi ile şablona veri aktarılabilir.
@Input() Kullanımında En İyi Uygulamalar
1. Tek Sorumluluk Prensibi (SRP): Bir bileşenin @Input() özellikleri, o bileşenin tek bir sorumluluğunu yansıtmalı ve aşırıya kaçmamalıdır. Çok fazla input, bileşenin karmaşıklaştığını ve muhtemelen birden fazla iş yaptığını gösterebilir.
2. Açık ve Anlamlı İsimlendirme: @Input() özelliklerine ne anlama geldiklerini açıkça belirten isimler verin. Takma adlar, özellikle çakışmaları önlemek veya dışarıdan daha sezgisel bir kullanım sağlamak için faydalıdır.
3. Tip Güvenliği: TypeScript’in gücünden yararlanın. @Input() özelliklerine doğru tipleri atayın. Karmaşık nesneler veya diziler için arayüzler (interfaces) veya tipler (types) tanımlayın.
interface User {
id: number;
name: string;
email: string;
}
@Input() user: User | undefined;
4. Değişmezlik (Immutability): Özellikle OnPush değişim algılama stratejisi kullanıyorsanız, nesne ve dizi @Input()‘larını güncellerken her zaman yeni referanslar oluşturun. Bu, Angular’ın değişiklikleri doğru bir şekilde algılamasını sağlar ve beklenmedik davranışları önler.
5. Varsayılan Değerler: @Input()‘lara varsayılan değerler atayarak bileşeninizi daha sağlam hale getirin. Bu, üst bileşenin bir değer sağlamayı unuttuğu durumlarda bile alt bileşenin düzgün çalışmasını sağlar.
6. Gereksiz ngOnChanges Kullanımından Kaçının: ngOnChanges güçlü bir araçtır, ancak her input değişikliğinde pahalı işlemler yapmak performansı düşürebilir. Sadece gerçekten ihtiyaç duyduğunuzda kullanın ve mümkünse ngOnInit veya setter’ları tercih edin.
7. Dokümantasyon: Bileşenlerinizin @Input() özelliklerini net bir şekilde belgeleyin. Hangi veriyi beklediğini, hangi tipleri kabul ettiğini ve isteğe bağlı olup olmadığını açıklayın. Bu, diğer geliştiricilerin (ve gelecekteki sizin) bileşenlerinizi kolayca anlamasına ve kullanmasına yardımcı olur.
8. Input Setter’ları ile Doğrulama ve Dönüştürme: Input değerlerini bileşeninize ulaşmadan önce doğrulamak veya dönüştürmek için setter’ları kullanın. Bu, bileşeninizin iç mantığını daha temiz tutar.
9. Gereksiz @Input() Kullanımından Kaçının: Eğer bir veri sadece bir kez, bileşen başlatılırken kullanılıyorsa ve daha sonra değişmesi beklenmiyorsa, bazen bir servis aracılığıyla veya @Input() yerine doğrudan bileşenin kendi içinde yüklenmesi daha uygun olabilir. Ancak bu, bileşenin sorumluluklarına ve uygulamanın genel mimarisine bağlıdır.
Sonuç
Angular’da @Input() dekorasyonu, bileşenler arası veri akışının temel ve en yaygın kullanılan mekanizmalarından biridir. Üst bileşenden alt bileşene veri gönderme yeteneği, modüler ve yeniden kullanılabilir bileşenler oluşturmanın anahtarıdır. @Input()‘ın basit kullanımından, yaşam döngüsü kancalarıyla etkileşimine, değişim algılama stratejilerine ve gelişmiş senaryolara kadar geniş bir yelpazeyi kapsayan bu mekanizmayı doğru bir şekilde anlamak ve uygulamak, Angular uygulamalarınızın performansını ve sürdürülebilirliğini önemli ölçüde artıracaktır. En iyi uygulamaları takip ederek, temiz, verimli ve kolayca yönetilebilir Angular bileşenleri geliştirebilirsiniz.
