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

Angular 22: Varsayılan OnPush, Stable Signal Forms ve Angular 12 ile Proje Farkı

Mert Özen 31 Eki 2026 7 dk 4 görüntülenme
Angular 22: Varsayılan OnPush, Stable Signal Forms ve Angular 12 ile Proje Farkı

Angular 22'de OnPush varsayılan, Signal Forms ve resource API'leri stable, HttpClient Fetch kullanıyor. Yılda bir major takvimini ve Angular 12 projesiyle dosya dosya farkı da inceliyoruz.

Sürüm notu: Bu yazı Angular 22 ile gelen yenilikleri anlatır; kodlar Angular 22 içindir.

Angular 22: Uzun Hazırlığın Meyvesi

Angular 21'de zoneless'ın varsayılan olduğunu, Signal Forms'un deneysel olarak geldiğini ve Vitest'in Karma'nın yerini aldığını gördük. 3 Haziran 2026'da çıkan Angular 22, son birkaç sürümde hazırlanan zemini tamamlıyor: Signal Forms ve resource API'leri stable oluyor, OnPush varsayılan change detection stratejisi hâline geliyor, HttpClient arka planda Fetch'e geçiyor.

Bu yazı diğerlerinden biraz daha uzun. Yeniliklerin ardından Angular 12 ile oluşturduğumuz bir projeyi bugün ng new ile oluşturulan bir projeyle dosya dosya karşılaştıracağız. Seriye başladığımız noktayla bugünün arasındaki mesafeyi en iyi o tablo gösteriyor.

OnPush Artık Varsayılan

Angular 12'de bir görev listesi component'i ve onu kullanan parent şöyle olabilirdi:

@Component({
  selector: 'app-task-list',
  template: `<li *ngFor="let task of tasks">{{ task }}</li>`
})
export class TaskListComponent {
  @Input() tasks: string[] = [];
}

// AppComponent
tasks = ['Market alışverişi'];

addTask(): void {
  this.tasks.push('Fatura öde');
}

Parent'taki butona basıldığında diziye yeni eleman eklenir ve liste ekranda büyürdü. Varsayılan strateji her seferinde bütün ağacı kontrol ettiği için dizinin referansının değişmemesi sorun değildi. Daha önce gördüğümüz change detection konusunda OnPush'u ayrıca, bilinçli olarak açmamız gerekiyordu.

Angular 22'de aynı kodu yeni sözdizimiyle yazalım:

import { Component, input } from '@angular/core';

@Component({
  selector: 'app-task-list',
  template: `
    @for (task of tasks(); track $index) {
      <li>{{ task }}</li>
    }
  `
})
export class TaskList {
  readonly tasks = input<string[]>([]);
}

Decorator'da changeDetection yok, ama bu component OnPush. Angular 22'de strateji belirtmeyen her component OnPush davranıyor. Parent hâlâ push ile aynı diziyi değiştiriyorsa liste ekranda büyümez: input'a giden referans aynı kaldığı için Angular bu component'i kontrol etmeye gerek görmez.

Çözüm, değişikliği Angular'ın görebileceği şekilde yapmak:

export class App {
  protected readonly tasks = signal(['Market alışverişi']);

  protected addTask(): void {
    this.tasks.update(list => [...list, 'Fatura öde']);
  }
}

Parent template'inde [tasks]="tasks()" yazıyorsun. Her eklemede yeni bir dizi oluşuyor, signal değişiyor ve child güncelleniyor. Bir component'in eski davranışa ihtiyacı varsa bunu açıkça söylüyorsun:

@Component({
  selector: 'app-task-list',
  changeDetection: ChangeDetectionStrategy.Eager,
  template: `...`
})

Bir önceki sürümde Default adının neden Eager'a çevrildiğini şimdi daha net görüyorsun: artık "varsayılan" olan o değil. ng update sırasında çalışan migration, strateji belirtmeyen mevcut component'lere ChangeDetectionStrategy.Eager ekliyor; yani yükseltme sonrası uygulaman aynı şekilde çalışmaya devam ediyor.

Signal Forms ve Resource API'leri Stable

Angular 21'de deneysel olarak tanıdığımız Signal Forms, Angular 22 ile stable oldu. Son hâli, 21.1'deki isim değişikliğiyle aynı:

import { Component, signal } from '@angular/core';
import { form, FormField, required, email } from '@angular/forms/signals';

@Component({
  selector: 'app-login',
  imports: [FormField],
  template: `
    <input type="email" [formField]="loginForm.email" />
    @if (loginForm.email().touched() && loginForm.email().invalid()) {
      <p>Geçerli bir e-posta gir</p>
    }
  `
})
export class Login {
  private readonly credentials = signal({ email: '', password: '' });
  protected readonly loginForm = form(this.credentials, (f) => {
    required(f.email, { message: 'E-posta zorunlu' });
    email(f.email);
  });
}

Stable demek, API'nin artık sürümden sürüme isim değiştirmeyeceğine dair bir söz demek. Angular 22 bunun yanında minDate/maxDate validator'ları, bir alanın doğrulamasını geciktiren debounce() ve sunucuya soran doğrulamalar için validateHttp() gibi eklemeler de getirdi.

Aynı sözü resource ailesi de aldı. Angular 19'dan beri experimental olan resource({ params, loader }), rxResource({ params, stream }) ve httpResource(() => url) artık stable. 19 ve 20'deki isim değişikliklerini takip ettiysen, gördüğün son şekil kalıcı olan.

HttpClient Artık Fetch Kullanıyor

Angular 12'de HttpClient'ı kullanabilmek için önce module'e eklemek gerekiyordu:

@NgModule({
  imports: [BrowserModule, HttpClientModule],
  // ...
})
export class AppModule { }

Angular 21'den beri HttpClient zaten root'ta hazır geliyordu. Angular 22'de değişen, arka plandaki motor: HttpClient artık istekleri XMLHttpRequest yerine tarayıcının Fetch API'siyle gönderiyor. Daha önce SSR için önerilen withFetch() artık varsayılan olduğu için deprecated oldu.

Senin tarafında çoğu zaman hiçbir şey değişmiyor; get, post ve interceptor'lar aynı. Fetch'in desteklemediği tek şey dosya yükleme ilerlemesi. Yükleme yüzdesini gösteren bir ekranın varsa eski motoru açıkça istiyorsun:

export const appConfig: ApplicationConfig = {
  providers: [provideHttpClient(withXhr())]
};

Bu yüzden reportProgress seçeneği de deprecated oldu; yerine reportUploadProgress ve reportDownloadProgress geldi. Güncelleme migration'ı, projende provideHttpClient() varsa davranış değişmesin diye withXhr() ekliyor.

Derleyici ve Build Tarafı: strictTemplates ve Webpack

Angular 12 projesinin tsconfig.json dosyasında şu satırlar vardı:

"angularCompilerOptions": {
  "enableI18nLegacyMessageIdFormat": false,
  "strictInjectionParameters": true,
  "strictInputAccessModifiers": true,
  "strictTemplates": true
}

Angular 22 ile oluşturulan projede strictTemplates satırı yok, çünkü artık varsayılan olarak açık. TypeScript 6 da strict ayarını varsayılan olarak açık getirdiği için "strict": true satırı da kayboldu. Satırlar gitti ama sıkı kontrol yerinde. Mevcut bir projede bu ayar kapalıysa, migration davranış değişmesin diye "strictTemplates": false ekliyor.

Build tarafında da bir dönem kapanıyor. Angular 12'de angular.json içindeki builder @angular-devkit/build-angular:browser idi ve arkada webpack çalışıyordu. Angular 17'den beri yeni projeler esbuild tabanlı application builder'ı kullanıyor; bugün bu builder @angular/build:application adıyla ayrı bir paketten geliyor. Angular 22 ile webpack tabanlı @angular-devkit/build-angular builder'ları deprecated oldu, Jest ve Web Test Runner builder'ları ise tamamen kaldırıldı. Hâlâ webpack ile build alan bir projen varsa geçiş planını şimdiden yapmanın tam zamanı.

Angular 12 ile Angular 22 Projesi: Dosya Dosya

Angular 12'de ng new ile oluşturduğumuz projenin iskeleti:

ilk-uygulama/
  .browserslistrc
  .editorconfig
  .gitignore
  angular.json
  karma.conf.js
  package.json
  README.md
  tsconfig.json
  tsconfig.app.json
  tsconfig.spec.json
  src/
    favicon.ico
    index.html
    main.ts
    polyfills.ts
    styles.css
    test.ts
    assets/
    environments/
      environment.ts
      environment.prod.ts
    app/
      app.module.ts
      app.component.ts
      app.component.html
      app.component.css
      app.component.spec.ts

Angular 22'de aynı komutun çıktısı:

ilk-uygulama/
  .editorconfig
  .gitignore
  .prettierrc
  .vscode/
  angular.json
  package.json
  README.md
  tsconfig.json
  tsconfig.app.json
  tsconfig.spec.json
  public/
    favicon.ico
  src/
    index.html
    main.ts
    styles.css
    app/
      app.ts
      app.html
      app.css
      app.spec.ts
      app.config.ts
      app.routes.ts

En büyük fark src/app klasöründe. app.module.ts yok; uygulama genelindeki provider'lar app.config.ts içinde duruyor. Angular 12'de routing'i seçersen ayrı bir app-routing.module.ts gelirdi, şimdi routing varsayılan ve route'lar sade bir dizi olarak app.routes.ts içinde. Kök component de app.component.ts yerine app.ts dosyasında, adı App.

main.ts bu değişimi tek bakışta anlatıyor:

// Angular 12
if (environment.production) {
  enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

// Angular 22
bootstrapApplication(App, appConfig)
  .catch((err) => console.error(err));

Kaybolan dosyaların her birinin bir hikâyesi var. polyfills.ts gitti, çünkü yüklenecek bir Zone.js kalmadı. karma.conf.js ve test.ts gitti, çünkü testleri Vitest koşturuyor. environments/ klasörü ve .browserslistrc Angular 15'ten beri varsayılan olarak üretilmiyor. src/assets yerini kök dizindeki public/ klasörüne bıraktı. Yeni gelenler ise .prettierrc ve VS Code ayarlarını taşıyan .vscode/ klasörü.

package.json da aynı hikâyeyi anlatıyor. Angular 12'de zone.js ~0.11.4, typescript ~4.3.5 ve bir düzine Karma/Jasmine paketi vardı. Angular 22'de zone.js hiç yok; geliştirme bağımlılıklarında @angular/build, vitest, jsdom, prettier ve typescript ~6.0.2 duruyor. Oluşan app.ts içinde standalone da changeDetection da yazmıyor; ikisi de artık varsayılan.

Yılda Bir Major Sürüm

Angular 12'den 22'ye kadar her altı ayda bir yeni major sürüm geldi; bu seride her yazının bir sürüme denk gelmesinin sebebi de buydu. Angular 22 ile takvim değişti: artık yılda bir major sürüm çıkıyor ve arada dört ila altı minor sürüm geliyor. Plana göre 22.3 Kasım 2026'da, Angular 23 ise 2027'nin ortalarında, Haziran civarında bekleniyor.

Destek süresi de buna göre ayarlandı: her major sürüm 12 ay aktif destek, ardından 12 ay LTS alıyor. Pratik bir not: Angular 20 kullanıyorsan LTS desteği 28 Kasım 2026'da bitiyor, yükseltme planını ertelememekte fayda var.

Angular 22'ye Geçiş: ng update

ng update @angular/core@22 @angular/cli@22

Angular 22, TypeScript 6.0 istiyor; 5.9 dahil eski sürümler desteklenmiyor. Node.js 20 desteği bitti; ^22.22.3, ^24.15.0 ya da ^26.0.0 serilerinden biri gerekiyor. Angular 12'deki bir projeyi buraya kadar getirmek istersen, CLI'nin birden fazla major sürümü tek seferde atlamana izin vermediğini hatırla: 13, 14, 15 diye tek tek ilerlemen gerekiyor.

Bu güncellemede çalışan migration'lar epey iş yapıyor: component'lere Eager ekliyor, gerekiyorsa withXhr() ve "strictTemplates": false koyuyor. Template'lerdeki ?. operatörü de artık TypeScript'teki gibi undefined döndürüyor; migration, eski davranışa bağlı ifadeleri özel bir fonksiyonla sararak koruyor. Komut bittikten sonra git diff çıktısını dikkatle oku.

Küçük Bir Deneme

npx @angular/cli@22 new deneme ile bir proje aç. CLI'nin artık routing ve zoneless sormadığına, onun yerine stil, SSR ve yapay zekâ araçlarıyla ilgili sorular sorduğuna dikkat et. Proje oluşunca klasör yapısını yukarıdaki Angular 22 ağacıyla karşılaştır.

Sonra TaskList örneğini kur. Parent'ta önce düz dizi ve push kullan, butona bas ve listenin büyümediğini gör. Ardından child'a changeDetection: ChangeDetectionStrategy.Eager ekleyip tekrar dene. Son olarak Eager'ı kaldır ve parent'ı signal ile update kullanacak şekilde yeniden yaz. Üç durumu yan yana görmek, OnPush'un mantığını oturtmanın en kısa yolu.

Seri burada bitmiyor. Takvim tutarsa 2027'nin ortasında Angular 23 gelecek ve yeni sürümler çıktıkça neyin değiştiğini aynı şekilde, Angular 12'deki hâliyle yan yana koyarak anlatmaya devam edeceğiz.