Angular 18 Signals Rehberi: Reaktif Programlama Devrimi (2025)

23.12.2025 10:01 Haber

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 Nedir? (What are Angular Signals?)

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

Signal Tanımı (Signal Definition)

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.

? Alesta Web İpucu:

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

? Neden Signals? RxJS'ten Farkı Nedir? (Why Signals? Difference from RxJS)

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

Signals'ın Avantajları (Advantages of Signals)

  • Basitlik: RxJS operatörlerini bilmeye gerek yok (no need to know RxJS operators)
  • Performans: Sadece değişen kısımlar render edilir (only changed parts render)
  • Type-safety: TypeScript ile mükemmel çalışır (perfect with TypeScript)
  • Okunabilirlik: Kod daha temiz ve anlaşılır (cleaner and more readable code)
  • Hafıza güvenliği: Subscribe/unsubscribe gerekmez (no subscribe/unsubscribe needed)

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

? Temel Kullanım: signal(), computed(), effect() (Basic Usage)

Angular Signals'ın 3 temel yapı taşı var:

1. signal() - Writable Signal (Yazılabilir Signal)

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

2. computed() - Computed Signal (Hesaplanan 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>

3. effect() - Side Effects (Yan Etkiler)

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ı:

  • LocalStorage'a otomatik kaydetme (auto-save to localStorage)
  • Analytics event gönderme (send analytics events)
  • Debugging ve logging
⚠️ effect() Kullanırken Dikkat:

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

? Signal-based Inputs (Angular 18 Yeniliği) (Angular 18 New Feature)

Angular 18'deki en büyük yenilik: @Input() decorator'ının signal versiyonu (signal version of @Input() decorator)!

Eski Yöntem (@Input Decorator):

export class UserCardComponent {
  @Input() username: string = '';
  @Input() age: number = 0;
}

Yeni Yöntem (Signal Inputs - Angular 18):

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"
/>
✅ Signal Inputs'un Avantajları:
  • Read-only (değiştirilemez, data flow güvenli / immutable, safe data flow)
  • Otomatik change detection optimizasyonu
  • computed() ile kolayca türev değerler oluşturma
  • Type-safety (tam tip güvenliği)

Alesta Web ekibi olarak yeni projelerde signal-based inputs kullanmanızı öneriyoruz (we recommend using signal-based inputs in new projects)!

? Pratik Örnekler (Practical Examples)

Örnek 1: Todo Uygulaması (Todo App)

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

Örnek 2: Filtrelenebilir Liste (Filterable List)

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 Birlikte Kullanımı (Using Signals with RxJS)

Signals ve RxJS birbirini tamamlar (complement each other). Angular bunları birleştirmek için helper fonksiyonlar sunar:

toSignal() - Observable'dan Signal'a (Observable to Signal)

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!
}

toObservable() - Signal'dan Observable'a (Signal to Observable)

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)
    });
  }
}
? Alesta Web Best Practice:

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

✨ Best Practices ve İpuçları (Best Practices and Tips)

1. Signal İsimlendirme (Signal Naming)

// ✅ İyi (Good)
const count = signal(0);
const userName = signal('');

// ❌ Kötü (Bad) - Observable gibi isimlendirme
const count$ = signal(0);  // $ sadece Observable için!

2. Immutability (Değiştirilemezlik)

// ❌ 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]);

3. Computed Signal'ları Cached (Önbellekli)

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(...);
});

4. effect() Sadece Side Effects İçin (Only for Side Effects)

// ✅ 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İ!
});

? RxJS'ten Signals'a Geçiş (Migrating from RxJS to Signals)

Alesta Web ekibi olarak adım adım geçiş stratejisi (step-by-step migration strategy):

Adım 1: Basit State'lerden Başlayın (Start with Simple State)

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

Adım 2: Template'leri Güncelleyin (Update Templates)

Önce:

<p>{{ count$ | async }}</p>

Sonra:

<p>{{ count() }}</p>

Adım 3: HTTP Çağrıları için toSignal() Kullanın

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 }}
}
⚠️ Dikkat:

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

? Kaynaklar ve Referanslar / Sources and References

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 Signals ile Reaktif Programlamaya Başlayın! (Start Reactive Programming with Signals!)

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:

  • ✅ Signals basit, performanslı reaktif state yönetimi (simple, performant reactive state)
  • ✅ signal(), computed(), effect() üç temel yapı taşı (three building blocks)
  • ✅ Angular 18'de signal-based inputs stabil (signal-based inputs stable in Angular 18)
  • ✅ RxJS ile birlikte çalışır (works together with RxJS)
  • ✅ Change detection otomatik optimize (change detection auto-optimized)
  • ✅ Memory leak riski yok (no memory leak risk)

Faydalı Linkler / Useful Links:

? Sonraki Adım:

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.

WM Tools
💫

WebMaster Tools

15 Profesyonel Araç