2026'da Angular Signals ve Computed: İnce Taneli Reaktivite ve Mülakat Soruları
Angular 20+ ile Angular Signals, computed, effect ve linkedSignal konularında uzmanlaşın. İnce taneli reaktivite desenleri ve mülakat hazırlığı.

Angular Signals, framework'ün ortaya çıkışından bu yana Angular'ın reaktivite modelindeki en önemli değişikliği temsil etmektedir. Angular 17'de tanıtılan ve şu anda Angular 20+'da varsayılan yaklaşım olan signals, gereksiz değişiklik algılama döngülerini ortadan kaldıran ince taneli reaktivite sağlar ve zoneless uygulamalarla sorunsuz entegre olur.
Signals, senkron ve glitch-free reaktif primitiflerdir. Computed signal, bağımlılıkları değiştiğinde yeniden hesaplanır ve değişiklik döngüsü başına kaç kez okunursa okunsun yalnızca bir kez hesaplanır.
Angular 20'de Signal Primitiflerini Anlamak
Angular'ın signal sistemi üç temel primitiften oluşur: signal(), computed() ve effect(). Her biri reaktif grafikte belirli bir amaca hizmet eder.
Yazılabilir bir signal, .set() veya .update() ile güncellenebilen bir değer tutar. Signal okunduğunda mevcut değer döndürülür ve ona bağımlı tüm computed signals veya effects değişikliklerden haberdar edilir.
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Count: {{ count() }}</p>
<p>Double: {{ doubleCount() }}</p>
<button (click)="increment()">+1</button>
`
})
export class CounterComponent {
// Writable signal with initial value
count = signal(0);
// Computed signal derives from count
// Only recalculates when count changes
doubleCount = computed(() => this.count() * 2);
increment(): void {
// .update() receives current value as argument
this.count.update(c => c + 1);
}
}RxJS BehaviorSubject'ten temel fark: signals senkron ve glitch-free'dir. count değiştiğinde, aynı senkron blokta birden fazla kez okunsa bile doubleCount tam olarak bir kez yeniden hesaplanır.
Computed Signals: Tembel Değerlendirme ve Memoization
Computed signals, bir bağımlılık değişene kadar sonuçlarını önbelleğe alır. Bu memoization otomatiktir, RxJS'de shareReplay veya distinctUntilChanged'ın manuel olarak eklenmesi gerekir.
import { Component, signal, computed } from '@angular/core';
interface User {
id: number;
name: string;
purchases: number[];
}
@Component({
selector: 'app-user-stats',
standalone: true,
template: `
<div>
<p>User: {{ user().name }}</p>
<p>Total spent: {{ totalSpent() | currency }}</p>
<p>Average purchase: {{ averagePurchase() | currency }}</p>
</div>
`
})
export class UserStatsComponent {
user = signal<User>({
id: 1,
name: 'Alice',
purchases: [99.99, 149.50, 29.99]
});
// Computed tracks only the properties it reads
totalSpent = computed(() => {
const purchases = this.user().purchases;
return purchases.reduce((sum, p) => sum + p, 0);
});
// Depends on another computed signal
averagePurchase = computed(() => {
const total = this.totalSpent();
const count = this.user().purchases.length;
return count > 0 ? total / count : 0;
});
}Computed signal tembel olarak değerlendirilir: hesaplama fonksiyonu signal ilk okunduğunda çalışır, tanımlandığında değil. Sonraki okumalar, bir bağımlılık geçersiz kılana kadar önbelleğe alınmış değeri döndürür.
Effect: Reaktif Grafikte Yan Etkiler
Effects, signal değişikliklerine yanıt olarak kod çalıştırır. Tüm senkron güncellemeler tamamlandıktan sonra, değişiklik algılama döngüsü sırasında asenkron olarak çalışırlar.
import { Component, signal, effect, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Component({
selector: 'app-theme-sync',
standalone: true,
template: `
<button (click)="toggleTheme()">Toggle Theme</button>
`
})
export class ThemeSyncComponent {
private document = inject(DOCUMENT);
isDarkMode = signal(false);
constructor() {
// Effect syncs signal state to DOM attribute
effect(() => {
const theme = this.isDarkMode() ? 'dark' : 'light';
this.document.documentElement.setAttribute('data-theme', theme);
});
// Effect for localStorage persistence
effect(() => {
localStorage.setItem('theme', this.isDarkMode() ? 'dark' : 'light');
});
}
toggleTheme(): void {
this.isDarkMode.update(dark => !dark);
}
}Effects bağımlılıkları dinamik olarak izler. Koşullu bir dal alınmazsa, yalnızca o dalda okunan signals koşul değişene kadar izlenmez.
Bir signal'dan diğerine veri kopyalamak için effect kullanmak bir tasarım sorunu olduğunu gösterir. Türetilmiş state için computed veya türetilmiş değerin yazılabilir olması gerektiğinde linkedSignal tercih edilmelidir.
linkedSignal: Yazılabilir Türetilmiş State
Angular 19'da tanıtılan ve Angular 20'de stabil hale gelen linkedSignal, kaynağı değiştiğinde sıfırlanan yazılabilir bir signal oluşturur. Computed'ın salt okunur olduğu ancak türetilmiş değerin yerel modifikasyona ihtiyaç duyduğu senaryoları çözer.
import { Component, signal, linkedSignal, computed } from '@angular/core';
@Component({
selector: 'app-pagination',
standalone: true,
template: `
<select (change)="pageSize.set(+$any($event.target).value)">
<option [value]="10">10 per page</option>
<option [value]="25">25 per page</option>
<option [value]="50">50 per page</option>
</select>
<p>Page {{ currentPage() }} of {{ totalPages() }}</p>
<button (click)="prevPage()" [disabled]="currentPage() === 1">Prev</button>
<button (click)="nextPage()" [disabled]="currentPage() === totalPages()">Next</button>
`
})
export class PaginationComponent {
totalItems = signal(243);
pageSize = signal(10);
totalPages = computed(() =>
Math.ceil(this.totalItems() / this.pageSize())
);
// linkedSignal resets to 1 when pageSize or totalItems changes
// but allows manual navigation via prevPage/nextPage
currentPage = linkedSignal(() => 1);
prevPage(): void {
this.currentPage.update(p => Math.max(1, p - 1));
}
nextPage(): void {
this.currentPage.update(p => Math.min(this.totalPages(), p + 1));
}
}linkedSignal olmadan bu desen, ya başka bir signal'a yazan effect (anti-pattern) ya da karmaşık RxJS orkestrasyonu gerektirirdi.
Angular mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Signal Tabanlı Component API'leri: input, model, viewChild
Angular 20, signal tabanlı component API'lerini stabil statüsüne taşır. Bunlar dekoratörleri, signal grafiğine entegre olan reaktif alternatiflerle değiştirir.
import {
Component,
input,
model,
output,
computed,
viewChild,
ElementRef
} from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<div #card class="card" [class.selected]="selected()">
<h3>{{ fullName() }}</h3>
<button (click)="toggle()">{{ selected() ? 'Deselect' : 'Select' }}</button>
</div>
`
})
export class UserCardComponent {
// Signal input replaces @Input()
firstName = input.required<string>();
lastName = input<string>('');
// model() creates two-way bindable signal
// Parent uses [(selected)]="parentSignal"
selected = model(false);
// Output emitter for events
cardClicked = output<void>();
// viewChild returns Signal<ElementRef | undefined>
cardElement = viewChild<ElementRef>('card');
// Computed derives from signal inputs
fullName = computed(() =>
`${this.firstName()} ${this.lastName()}`.trim()
);
toggle(): void {
this.selected.update(s => !s);
this.cardClicked.emit();
}
}Signal inputs daha iyi tür güvenliği sağlar: input.required() InputSignal<T> döndürürken, varsayılan sağlanmadıkça input() InputSignal<T | undefined> döndürür.
Signals vs RxJS: Yük Altında Performans
Angular 21+'daki kıyaslamalar, signals'ın UI yoğun senaryolarda değişiklik algılama yükünü azalttığını göstermektedir. Yoğun güncellemeler altında, signals RxJS tabanlı binding'lere kıyasla daha temiz bellek profilleri ve daha düşük CPU kullanımı sergilemiştir.
Sorumlulukların ayrımı açıktır:
| Kullanım Durumu | Önerilen Yaklaşım |
|---|---|
| Yerel component state | signal() |
| Türetilmiş UI değerleri | computed() |
| Harici API'lerle senkronizasyon | effect() |
| HTTP istekleri | RxJS Observable + toSignal() |
| WebSocket akışları | Operatörlerle RxJS |
| Debounced kullanıcı girişi | RxJS debounceTime, sonra toSignal() |
| Karmaşık async orkestrasyon | RxJS pipelines |
RxJS, async akışlar, iptal ve backpressure için araç olmaya devam eder. Signals senkron state'i yönetir.
RxJS Birlikte Çalışabilirliği: toSignal ve toObservable
Angular, signals ve observables'ı köprülemek için @angular/core/rxjs-interop'ta yardımcı araçlar sağlar.
import { Component, signal, inject } from '@angular/core';
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
import { debounceTime, switchMap, distinctUntilChanged } from 'rxjs';
interface SearchResult {
id: number;
title: string;
}
@Component({
selector: 'app-search',
standalone: true,
template: `
<input
[value]="query()"
(input)="query.set($any($event.target).value)"
placeholder="Search..."
/>
@if (results(); as items) {
<ul>
@for (item of items; track item.id) {
<li>{{ item.title }}</li>
}
</ul>
}
`
})
export class SearchComponent {
private http = inject(HttpClient);
query = signal('');
// Convert signal to observable for RxJS operators
private query$ = toObservable(this.query);
// RxJS pipeline with debounce, then back to signal
results = toSignal(
this.query$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(q => q.length > 2
? this.http.get<SearchResult[]>(`/api/search?q=${q}`)
: []
)
),
{ initialValue: [] as SearchResult[] }
);
}toSignal oluşturulduğunda abone olur ve component yok edildiğinde aboneliği iptal eder. initialValue seçeneği signal'dan undefined türünü ortadan kaldırır.
Mülakat Soruları: Angular Signals
2026'daki teknik mülakatlar sıklıkla signal anlayışını test eder. İşte senior adayları ayırt eden kalıplar.
S: Computed signal hata fırlattığında ne olur?
Hata, onu okuyan her effect veya computed'a yayılır. Angular hataları önbelleğe almaz, bir sonraki okumada hesaplama yeniden denenir. Üretim kodu, computed fonksiyonu içinde hataları yönetmelidir.
S: Computed içinde bir signal'a yazılabilir mi?
Hayır. Computed içinde .set() veya .update() çağırmaya çalışmak hata fırlatır. Computed fonksiyonları saf olmalıdır. Türetilmiş bir değerin ayrıca yazılabilir olması gerekiyorsa linkedSignal kullanılmalıdır.
S: Effects koşullardaki signal okumalarını nasıl yönetir?
// Dynamic dependency tracking example
const showDetails = signal(false);
const userDetails = signal({ name: 'Alice', email: 'alice@example.com' });
effect(() => {
if (showDetails()) {
// userDetails is only tracked when showDetails is true
console.log('Details:', userDetails().email);
}
});Effects, en son yürütmeye dayalı olarak bağımlılıkları izler. showDetails false ise, userDetails'teki değişiklikler effect'i tetiklemez.
S: Signal eşitliği nasıl çalışır?
Varsayılan olarak signals eşitlik için Object.is kullanır. Nesneler için bu referans eşitliği anlamına gelir. Özel eşitlik sağlanabilir:
const user = signal(
{ id: 1, name: 'Alice' },
{ equal: (a, b) => a.id === b.id }
);Computed signals'ın memoize edildiğini ve tembel değerlendirildiğini belirten adaylar, temel API kullanımının ötesinde anlayış sergiler. linkedSignal vs computed'ın ne zaman kullanılacağını tartışmak Angular 19+ kalıplarına aşinalığı gösterir.
Değişiklik Algılama: Signals ve Zoneless Angular
Angular 22, yeni projeler için varsayılan olarak zoneless değişiklik algılaması kullanır. Signals bunun merkezindedir: state'in ne zaman değiştiğini Angular'a tam olarak bildirir ve Zone.js'in async API'leri yaması gereksinimini ortadan kaldırır.
import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-zoneless-counter',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<p>Count: {{ count() }}</p>
<button (click)="increment()">+1</button>
`
})
export class ZonelessCounterComponent {
count = signal(0);
increment(): void {
// Signal update triggers change detection
// No Zone.js required
this.count.update(c => c + 1);
}
}Signals ile Angular, hangi component'lerin hangi state'e bağlı olduğunu tam olarak izler. Signal güncellemesi, tüm ağaç için değil yalnızca etkilenen component'ler için değişiklik algılaması planlar.
Resource API: Signals Olarak Async Veriler
Angular 20'nin Resource API'si (resource() ve rxResource()) async verileri bildirimsel olarak signals'a yükler ve manuel abonelik kalıplarını değiştirir.
import { Component, signal, computed } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-profile',
standalone: true,
template: `
@if (userResource.isLoading()) {
<p>Loading...</p>
}
@if (userResource.error()) {
<p>Error: {{ userResource.error() }}</p>
}
@if (userResource.value(); as user) {
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
}
`
})
export class UserProfileComponent {
private http = inject(HttpClient);
userId = signal(1);
// Declarative async resource
// Refetches when userId changes
userResource = rxResource({
request: () => this.userId(),
loader: ({ request: id }) =>
this.http.get<User>(`/api/users/${id}`)
});
loadUser(id: number): void {
this.userId.set(id);
}
}Resource otomatik olarak yükleme state'ini, hataları ve çözümlenen değeri izler. Request signal değiştiğinde yeniden veri çeker.
Signal Mimarisi İçin En İyi Uygulamalar
Daha büyük uygulamalarda signals'ı yapılandırmak disiplin gerektirir. Bu kalıplar component'lerden services'e ölçeklenir.
İlgili signals'ı bir arada tutun: Birlikte değişen signals'ı gruplandırın. Form component'i alan signals'ını servisler arasında dağıtmak yerine bir arada tutar.
Paylaşılan state'i servislere taşıyın: Birden fazla component aynı state'e ihtiyaç duyduğunda, signal enjekte edilebilir bir servise taşınmalıdır:
import { Injectable, signal, computed } from '@angular/core';
interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
@Injectable({ providedIn: 'root' })
export class CartService {
private items = signal<CartItem[]>([]);
// Expose read-only signal to consumers
readonly cartItems = this.items.asReadonly();
readonly totalPrice = computed(() =>
this.items().reduce((sum, item) => sum + item.price * item.quantity, 0)
);
readonly itemCount = computed(() =>
this.items().reduce((sum, item) => sum + item.quantity, 0)
);
addItem(item: Omit<CartItem, 'quantity'>): void {
this.items.update(items => {
const existing = items.find(i => i.id === item.id);
if (existing) {
return items.map(i =>
i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
);
}
return [...items, { ...item, quantity: 1 }];
});
}
}Effect yerine computed tercih edin: Bir değer türetilebiliyorsa computed kullanın. Effects, reaktif graftan ayrılan yan etkiler içindir: DOM manipülasyonu, localStorage, analytics, ağ istekleri.
RxJS'den Signals'a Geçiş
Eski Angular kod tabanları kademeli olarak geçiş yapabilir. RxJS birlikte çalışabilirlik araçları signals ve observables'ın bir arada var olmasına izin verir.
// Before: RxJS-based
@Component({ ... })
export class LegacyComponent {
private userSubject = new BehaviorSubject<User | null>(null);
user$ = this.userSubject.asObservable();
userName$ = this.user$.pipe(
map(u => u?.name ?? 'Guest')
);
}
// After: Signal-based
@Component({ ... })
export class ModernComponent {
user = signal<User | null>(null);
userName = computed(() => this.user()?.name ?? 'Guest');
}Async işlemler için RxJS'i pipeline için koruyun ve sınırda dönüştürün:
// Hybrid approach
private search$ = toObservable(this.searchQuery).pipe(
debounceTime(300),
switchMap(q => this.api.search(q))
);
results = toSignal(this.search$, { initialValue: [] });Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Angular Signals Mülakat Sorularına Hazırlık
- Signals senkron primitiflerdir; RxJS async akışları yönetir
- Computed signals tembeldir ve memoize edilir, yalnızca bağımlılıklar değiştiğinde yeniden hesaplanır
- Effects değişiklik algılama sırasında asenkron olarak çalışır, hemen değil
- linkedSignal, effects olmadan yazılabilir türetilmiş state sorununu çözer
- Signal inputs (
input(),model()) dekoratörleri reaktif alternatiflerle değiştirir toSignalvetoObservablekademeli geçiş için signals ve RxJS'i köprüler- Zoneless Angular, ince taneli değişiklik algılama için signals'a dayanır
- Resource API (
rxResource) async state'i bildirimsel olarak yönetir - Resmi Angular signals rehberi API detayları ve en iyi uygulamalar için yetkili referans olmaya devam eder
Angular kodundaki hatayı bulabilir misin?
Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Yazan:
Anthony Fillion-MailletSharpSkill kurucusu
10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.
17 Eylül 2026 tarihinde güncellendi
Paylaş
İlgili makaleler

2026'da İleri Düzey Angular Dependency Injection: Providers, Tokens ve Mülakat Soruları
Angular'da ileri düzey Dependency Injection mekanizmalarına kapsamlı bir bakış. Providers, InjectionToken, injector hiyerarşisi ve teknik mülakatlarda karşılaşılan sorular.

Angular Control Flow Sözdizimi 2026: @if, @for, @switch ve Mülakat Soruları
Angular'da blok tabanlı control flow sözdizimi için kapsamlı rehber. @if, @for, @switch kullanımını pratik kod örnekleriyle öğrenin ve teknik mülakatlarına hazırlanın.

Angular HttpClient ve Interceptor'lar 2026: HTTP İstek Yönetimi ve Mülakat Soruları
Angular 20 fonksiyonel interceptor'larına kapsamlı bir rehber: kimlik doğrulama token enjeksiyonundan üstel geri çekilme ile yeniden deneme mantığına kadar. Mülakat soruları ve production kalıpları içerir.