Ulaşım
- Adres:Batıkent Mh. 8910 Sk. 6. Etap 1H No: 18 Yeni Toki Eyyübiye / Şanlıurfa (Yeni Alım Satım Karşısı)
- Telefon:0 (545) 528 88 93
- eMail: info@alestaweb.com
Angular 18 Signals, Angular'ın reaktif programlama devrimi (reactive programming revolution)! Change detection'ı optimize eden, %50 daha performanslı kod yazmanızı sağlayan bu yeni özellik (new feature) artık stabil. Alesta Web olarak Angular Signals'ın ne olduğunu, nasıl kullanıldığını ve neden RxJS'ten daha basit olduğunu (why it's simpler than RxJS) detaylarıyla anlatacağız.
Angular Signals, Angular 16'dan beri gelen (introduced in Angular 16), Angular 18'de stabil hale gelen reaktif state yönetimi sistemidir (reactive state management system). Basitçe söylemek gerekirse: bir değer değiştiğinde otomatik olarak ilgili UI bileşenlerini güncelleyen akıllı değişkenler (smart variables that auto-update UI).
import { signal } from '@angular/core';
export class MyComponent {
// Basit bir signal oluşturma (create a simple signal)
count = signal(0);
increment() {
this.count.set(this.count() + 1); // Değeri güncelle (update value)
}
}
Template'de kullanım:
<p>Sayı: {{ count() }}</p>
<button (click)="increment()">Artır (Increment)</button>
Alesta Web ekibi olarak Signals'ı şöyle tanımlıyoruz: Angular'ın change detection mekanizmasını daha akıllı, daha hızlı ve daha öngörülebilir hale getiren (more predictable) modern bir çözüm.
Signal bir "wrapper" (sarmalayıcı). Değeri okumak için count() gibi fonksiyon olarak çağırırsınız. Bu sayede Angular değişiklikleri izleyebilir (track changes).
Angular geliştiriciler yıllardır RxJS Observable'ları kullanıyor. Peki neden Signals'a ihtiyaç duyuldu?
| Özellik / Feature | Signals | RxJS Observables |
|---|---|---|
| Öğrenme Eğrisi | Çok Düşük (very low) | Yüksek (steep learning curve) |
| Performans | Çok Hızlı (granular updates) | Hızlı ama overhead var |
| Change Detection | Otomatik optimize (auto-optimized) | Manuel optimizasyon gerekli |
| Memory Leaks | Riski yok (no risk) | Unsubscribe gerekli |
| Asenkron İşlemler | RxJS ile birlikte kullan | Mükemmel (perfect for async) |
| Template Kullanımı | {{ count() }} - basit | {{ count$ | async }} - pipe gerekli |
Alesta Web deneyimlerine göre: Basit state yönetimi için Signals, karmaşık asenkron işlemler için RxJS kullanın. İkisi birbirinin alternatifi değil, tamamlayıcısıdır (they are complementary, not alternatives)!
Angular Signals'ın 3 temel yapı taşı var:
import { signal } from '@angular/core';
export class CounterComponent {
count = signal(0); // Başlangıç değeri 0 (initial value 0)
increment() {
// Yöntem 1: set() ile direkt değer atama
this.count.set(10);
// Yöntem 2: update() ile mevcut değeri kullanma
this.count.update(value => value + 1);
}
getCount() {
return this.count(); // Signal'ı okuma (read signal)
}
}
Diğer signal'lardan türetilen, otomatik güncellenen signal (auto-updating derived signal):
import { signal, computed } from '@angular/core';
export class ShoppingCartComponent {
items = signal([
{ name: 'Ürün 1', price: 100, quantity: 2 },
{ name: 'Ürün 2', price: 50, quantity: 3 }
]);
// Otomatik hesaplanan toplam (auto-calculated total)
total = computed(() => {
return this.items().reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
});
// items değişince total otomatik güncellenir! (auto-updates!)
}
Template'de:
<p>Toplam: {{ total() }} TL</p>
Signal değiştiğinde otomatik çalışan fonksiyon (function that runs when signal changes):
import { signal, effect } from '@angular/core';
export class LoggerComponent {
count = signal(0);
constructor() {
// count her değiştiğinde konsola yaz (log whenever count changes)
effect(() => {
console.log('Count değişti! Yeni değer:', this.count());
// API çağrısı, localStorage kaydetme vb. yapabilirsiniz
});
}
}
Örnek kullanım senaryoları:
effect() içinde signal güncellemesi yapmayın (don't update signals inside effect)! Bu sonsuz döngüye neden olabilir (may cause infinite loop).
Angular 18'deki en büyük yenilik: @Input() decorator'ının signal versiyonu (signal version of @Input() decorator)!
export class UserCardComponent {
@Input() username: string = '';
@Input() age: number = 0;
}
import { Component, input } from '@angular/core';
@Component({
selector: 'app-user-card',
template: `
<div>
<h3>{{ username() }}</h3>
<p>Yaş: {{ age() }}</p>
<p>Yetişkin mi: {{ isAdult() ? 'Evet' : 'Hayır' }}</p>
</div>
`
})
export class UserCardComponent {
// Signal-based inputs (read-only)
username = input<string>('');
age = input<number>(0);
// Computed signal ile türetme (derive with computed)
isAdult = computed(() => this.age() >= 18);
}
Kullanımı (parent component'te):
<app-user-card [username]="'Ahmet'" [age]="25" />
Alesta Web ekibi olarak yeni projelerde signal-based inputs kullanmanızı öneriyoruz (we recommend using signal-based inputs in new projects)!
import { Component, signal, computed } from '@angular/core';
interface Todo {
id: number;
text: string;
completed: boolean;
}
@Component({
selector: 'app-todo',
template: `
<input [(ngModel)]="newTodoText" />
<button (click)="addTodo()">Ekle (Add)</button>
<p>Tamamlanan: {{ completedCount() }} / {{ todos().length }}</p>
<ul>
@for (todo of todos(); track todo.id) {
<li>
<input type="checkbox"
[checked]="todo.completed"
(change)="toggleTodo(todo.id)" />
{{ todo.text }}
</li>
}
</ul>
`
})
export class TodoComponent {
todos = signal<Todo[]>([]);
newTodoText = '';
// Tamamlanan todo sayısı (completed todos count)
completedCount = computed(() =>
this.todos().filter(t => t.completed).length
);
addTodo() {
this.todos.update(todos => [
...todos,
{ id: Date.now(), text: this.newTodoText, completed: false }
]);
this.newTodoText = '';
}
toggleTodo(id: number) {
this.todos.update(todos =>
todos.map(t => t.id === id ? {...t, completed: !t.completed} : t)
);
}
}
export class ProductListComponent {
allProducts = signal([
{ id: 1, name: 'Laptop', category: 'Electronics', price: 5000 },
{ id: 2, name: 'Kitap', category: 'Books', price: 50 },
{ id: 3, name: 'Telefon', category: 'Electronics', price: 3000 }
]);
searchTerm = signal('');
selectedCategory = signal('All');
// Otomatik filtrelenen liste (auto-filtered list)
filteredProducts = computed(() => {
const products = this.allProducts();
const search = this.searchTerm().toLowerCase();
const category = this.selectedCategory();
return products.filter(p => {
const matchesSearch = p.name.toLowerCase().includes(search);
const matchesCategory = category === 'All' || p.category === category;
return matchesSearch && matchesCategory;
});
});
}
Avantaj: searchTerm veya selectedCategory değiştiğinde filteredProducts otomatik güncellenir (auto-updates)!
Signals ve RxJS birbirini tamamlar (complement each other). Angular bunları birleştirmek için helper fonksiyonlar sunar:
import { toSignal } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';
export class TimerComponent {
// RxJS Observable
timer$ = interval(1000);
// Signal'a dönüştür (convert to signal)
timerSignal = toSignal(this.timer$, { initialValue: 0 });
// Template'de kullanımı çok basit (very simple in template)
// {{ timerSignal() }} - async pipe gerekmez!
}
import { toObservable } from '@angular/core/rxjs-interop';
import { debounceTime, distinctUntilChanged } from 'rxjs';
export class SearchComponent {
searchTerm = signal('');
// Signal'ı Observable'a çevir (convert signal to observable)
searchTerm$ = toObservable(this.searchTerm);
constructor() {
// RxJS operatörlerini kullan (use RxJS operators)
this.searchTerm$.pipe(
debounceTime(300),
distinctUntilChanged()
).subscribe(term => {
console.log('Arama:', term);
// API çağrısı yap (make API call)
});
}
}
HTTP çağrıları için hala RxJS kullanın (still use RxJS for HTTP). Sonucu toSignal() ile signal'a çevirin. Böylece her iki dünyanın da avantajlarından yararlanırsınız (get benefits of both worlds)!
// ✅ İyi (Good)
const count = signal(0);
const userName = signal('');
// ❌ Kötü (Bad) - Observable gibi isimlendirme
const count$ = signal(0); // $ sadece Observable için!
// ❌ YANLIŞ (WRONG) - Array'i direkt değiştirme this.items().push(newItem); // Çalışmaz! (won't work!) // ✅ DOĞRU (CORRECT) - Yeni array oluştur this.items.update(items => [...items, newItem]);
computed() sonuçları cache'lenir. Bağımlılıklar değişmezse yeniden hesaplanmaz (won't recalculate if dependencies don't change):
// Performanslı! (Performant!)
const expensiveCalculation = computed(() => {
// Sadece items değiştiğinde çalışır (only runs when items change)
return this.items().reduce(...);
});
// ✅ Doğru kullanım (Correct usage)
effect(() => {
localStorage.setItem('count', this.count().toString());
});
// ❌ Yanlış - signal güncelleme yapma! (Don't update signals!)
effect(() => {
this.anotherSignal.set(this.count() * 2); // SONSUZ DÖNGÜ RİSKİ!
});
Alesta Web ekibi olarak adım adım geçiş stratejisi (step-by-step migration strategy):
Önce (Before - RxJS):
export class Component {
private countSubject = new BehaviorSubject(0);
count$ = this.countSubject.asObservable();
increment() {
this.countSubject.next(this.countSubject.value + 1);
}
}
Sonra (After - Signals):
export class Component {
count = signal(0);
increment() {
this.count.update(v => v + 1);
}
}
Önce:
<p>{{ count$ | async }}</p>
Sonra:
<p>{{ count() }}</p>
export class UserComponent {
private http = inject(HttpClient);
// HTTP çağrısı (RxJS kalır / RxJS stays)
user$ = this.http.get('/api/user');
// Signal'a çevir (convert to signal)
user = toSignal(this.user$);
// Template'de: {{ user()?.name }}
}
Tüm kodu bir anda değiştirmeyin (don't change all code at once)! RxJS ve Signals birlikte çalışabilir. Yavaş yavaş geçiş yapın (migrate gradually).
Bu makalede kullanılan bilgiler aşağıdaki güvenilir kaynaklardan alınmıştır (information used in this article is from the following reliable sources):
Alesta Web olarak tüm bilgileri Angular 18 ile doğruladık ve test ettik (we verified and tested all information with Angular 18).
Angular 18 Signals, modern Angular geliştirmenin (modern Angular development) temelidir. RxJS'in karmaşıklığından sıkıldıysanız, performans sorunları yaşıyorsanız veya daha temiz kod yazmak istiyorsanız (want to write cleaner code) - Signals tam size göre! Alesta Web olarak Angular projelerinizde size yardımcı olabiliriz. alestaweb.com üzerinden iletişime geçin!
Hızlı Özet / Quick Summary:
Faydalı Linkler / Useful Links:
Mevcut Angular projenizde bir component'i Signals'a geçirin (migrate one component to Signals). Performans farkını kendiniz görün! Alesta Web ekibi başarılar diler!
© 2025 AlestaWeb - Tüm hakları saklıdır. Bu rehber Angular 18+ için güncellenmiştir.