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

Angular 13 ile Gelenler: ng update ile Sürüm Yükseltme, Ivy ve Yeni Dinamik Component API'si

Mert Özen 31 Eki 2026 9 dk 2 görüntülenme
Angular 13 ile Gelenler: ng update ile Sürüm Yükseltme, Ivy ve Yeni Dinamik Component API'si

Angular 12'den 13'e ng update ile adım adım geçiyoruz. View Engine'in kaldırılması, IE11'e veda, factory'siz createComponent, kalıcı build önbelleği ve Node/TypeScript gereksinimleri.

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

Angular 12'den Sonra Neler Oldu?

Buraya kadar her şeyi Angular 12 üzerine kurduk: NgModule'ler, constructor injection, class-based guard'lar, *ngIf ve *ngFor. Bu temel hâlâ geçerli ve göreceğin gibi Angular onu bir gecede çöpe atmadı. Ama framework yerinde de saymadı. Yıllarca her altı ayda bir yeni major sürüm çıktı ve her biri bir şeyleri biraz daha sadeleştirdi.

Bu yazıdan itibaren Angular'ı sürüm sürüm takip edeceğiz. Her seferinde önce "Angular 12'de bunu böyle yapıyorduk" diyecek, sonra aynı işin yeni sürümde nasıl yapıldığına bakacağız. İlk durak 3 Kasım 2021'de çıkan Angular 13. Yeniliklere geçmeden önce de sürüm atlamamızı sağlayacak aracı tanıyalım: ng update.

ng update Tam Olarak Ne Yapar?

Bir Angular projesini yükseltmek, package.json'daki sürüm numaralarını değiştirip npm install demekten ibaret değil. Yeni sürümde bir API'nin imzası değişmiş, bir ayar kaldırılmış, bir config dosyasına yeni bir satır gerekiyor olabilir. Bunları tek tek bulup elle düzeltmek saatler sürer.

ng update işi iki adımda halleder. Önce Angular paketlerini hedef sürüme çeker ve package.json'ı günceller. Ardından o paketlerle birlikte gelen migration'ları çalıştırır. Migration, kodunu okuyup yeni sürüme uygun hâle getiren küçük bir script'tir; Angular ekibi değiştirdiği ya da kaldırdığı şeylerin çoğu için bir tane yazar. İyi bir nakliye firması gibi düşün: eşyaları yeni eve taşımakla kalmaz, prizlerin yerine göre kabloları da yeniden döşer.

Bir kuralı baştan bil: ng update bir seferde yalnızca bir major sürüm atlar. 12'den doğrudan 15'e geçmeye kalkarsan CLI şuna benzer bir mesajla durur:

Updating multiple major versions of '@angular/core' at once is not supported. Please migrate each major version individually.

Sebebi basit. Her sürümün migration'ları, bir önceki sürümün kod yapısını bekler; 13'ün migration'ları çalışmadan 14'ünkiler doğru sonuç veremez. Yol hep aynı: 12'den 13'e, 13'ten 14'e, oradan 15'e. Merdiveni ikişer ikişer çıkmak yok.

Adım Adım 12'den 13'e

İlk iş, projeyi temiz bir git durumuna getirmek. Yükseltme onlarca dosyaya dokunabilir ve bir şey ters giderse dönebileceğin sağlam bir nokta lazım:

git status
git add .
git commit -m "chore: state before Angular 13 update"

Bunu yapmasan da ng update seni durdurur; commit edilmemiş değişiklik varken çalışmayı reddeder. --allow-dirty diye bir kaçış kapısı var ama kullanmanı önermem. Sonra hangi değişikliğin yükseltmeden, hangisinin senden geldiğini ayırt edemezsin.

İkinci iş Node sürümü. Seride nvm ile Node 16 kurmuştuk. Node 16, Angular 12'nin resmi listesinde yoktu ama pratikte sorunsuz çalışıyordu; Angular 13 ile resmi destek listesine giriyor. Yine de terminalde doğru sürümde olduğundan emin ol:

nvm use 16
node -v

Çıktı v16.10 ya da üstü olmalı. Proje köküne içinde sadece 16 yazan bir .nvmrc dosyası koyarsan, o klasörde argümansız nvm use demen yeter. İlerleyen sürümlerde Node gereksinimi yükseldiğinde aynı yolla nvm install ile yeni sürüme geçeceğiz; kahve makinesi aynı kalacak, kapsül değişecek.

Şimdi neyin güncellenebileceğine bakalım:

ng update

Argümansız ng update hiçbir şeyi değiştirmez. Projedeki Angular paketlerini tarar ve yükseltilebilecekleri, çalıştırman gereken komutla birlikte listeler. Asıl yükseltme şu komutla yapılır:

ng update @angular/core@13 @angular/cli@13

Sürümü @13 diye açıkça yazmak önemli. Yazmazsan ng update en son sürümü hedefler ve yukarıdaki "birden fazla major" hatasına çarparsın. Komut çalışırken önce paketlerin güncellendiğini, ardından migration'ların tek tek çalıştığını gösteren satırlar akar. Projende Angular Material gibi Angular'a bağlı başka paketler varsa onları da aynı komuta ekle, örneğin @angular/material@13.

Migration'lar bitince üç şeyi kontrol et: ng serve ile uygulama açılıyor mu, ng build hatasız bitiyor mu, ng test yeşil mi. Hepsi tamamsa git diff ile nelerin değiştiğine göz at ve commit'le. Bu commit bir sonraki yükseltmenin başlangıç noktası olacak. Her major geçişten önce angular.dev/update-guide adresindeki etkileşimli rehbere de bakmaya değer; hangi sürümden hangisine geçtiğini seçtiğinde elle yapman gereken adımları listeler.

Angular 13'ün Node ve TypeScript Beklentisi

Angular 13'ün resmi Node aralığı şöyle: 12.20 ve üstü, 14.15 ve üstü ya da 16.10 ve üstü. TypeScript tarafında Angular 13.0 en az TypeScript 4.4 ister; 13.x'in sonraki sürümleri 4.6'ya kadar destekler. Angular 12 projesi TypeScript 4.3 ile gelir, yani bu adımda TypeScript de bir basamak çıkmak zorunda.

ng update çoğu durumda TypeScript'i de uyumlu bir sürüme çeker. Yine de yükseltmeden sonra package.json'daki typescript satırına bir bak. Sürüm uymuyorsa Angular derleyicisi işe başlamadan, hangi aralığı beklediğini söyleyen bir hata verir.

Bir de RxJS notu: Angular 13 ile RxJS 7 resmi olarak destekleniyor. Angular 12 projesi RxJS 6.6 ile gelir ve 6.x ile devam edebilirsin. RxJS 7'ye geçmek zorunlu değil; istersen ayrıca ele alacağın bir iş.

View Engine Tarihe Karıştı

Angular'ın içinde, template'lerini tarayıcının çalıştırabileceği JavaScript koduna çeviren bir derleyici ve render motoru var. Uzun süre bu işi View Engine adlı motor yaptı. Angular 9'da varsayılan motor Ivy oldu, Angular 12'de View Engine deprecated olarak işaretlendi ve Angular 13'te tamamen kaldırıldı. Artık Angular yalnızca Ivy ile çalışıyor.

Bunu bir arabadaki iki motor gibi düşün. Yıllarca eski motor da kaputun altında yedekte bekletildi, çünkü bazı parçalar hâlâ ona göre üretiliyordu. Angular 13 o eski motoru söküp attı. Ekibin iki ayrı motoru birlikte ayakta tutma yükü kalktı ve sonraki sürümlerde göreceğimiz pek çok yenilik bu sayede mümkün oldu.

Uygulama geliştiren biri olarak senin için günlük hayatta pek bir şey değişmez; Angular 12'de de zaten Ivy kullanıyordun. Değişiklik daha çok kütüphane yazanları ilgilendiriyor. Angular kütüphaneleri artık "partial Ivy" denen formatta yayınlanıyor ve CLI'nin migration'ı senin kütüphane projelerini de bu moda geçiriyor. Hâlâ eski View Engine formatında yayınlanmış bir kütüphane kullanıyorsan, ngcc adlı uyumluluk aracı onu şimdilik Ivy'ye çeviriyor. Bu köprü de ileride kaldırılacak, o yüzden bağımlılıklarını gözden geçirmek için iyi bir zaman.

Internet Explorer 11'e Veda

Angular 12'de IE11 desteği deprecated olarak işaretlenmişti. Angular 13 bu desteği tamamen bıraktı. İlk bakışta "zaten kimse IE kullanmıyor" diyebilirsin ama bu kararın build çıktısına doğrudan etkisi var.

Eski tarayıcıları desteklemek için CLI, differential loading denen bir yöntemle aynı uygulamanın hem modern hem de eski ES5 sürümünü üretebiliyordu. CLI 13 ile differential loading kaldırıldı; artık yalnızca modern tarayıcılar için tek bir paket çıkıyor. ng update sırasında çalışan bir migration da polyfills.ts dosyandan sadece Internet Explorer için gereken polyfill'leri temizliyor.

Müşterin hâlâ IE11 istiyorsa durum net: o proje Angular 12'de kalmak zorunda. Bunu yükseltmeye başlamadan önce konuşmak, yarı yolda fark etmekten çok daha ucuz.

Dinamik Component Oluşturmak Kısaldı

Bazen bir component'i template'e önceden yazmak yerine çalışma anında, koddan oluşturmak istersin. Bir bildirim kutusu, bir modal ya da kullanıcının seçimine göre değişen bir widget. Elimizde basit bir uyarı component'i olsun:

@Component({
  selector: 'app-alert',
  template: `<div class="alert">{{ message }}</div>`
})
export class AlertComponent {
  message = '';
}

Angular 12'de bunu dinamik olarak ekrana basmak için önce bir factory üretmen gerekiyordu:

import { Component, ComponentFactoryResolver, ViewChild, ViewContainerRef } from '@angular/core';
import { AlertComponent } from './alert/alert.component';

@Component({
  selector: 'app-root',
  template: `
    <button (click)="showAlert()">Kaydet</button>
    <ng-container #host></ng-container>
  `
})
export class AppComponent {
  @ViewChild('host', { read: ViewContainerRef }) host!: ViewContainerRef;

  constructor(private resolver: ComponentFactoryResolver) { }

  showAlert(): void {
    this.host.clear();
    const factory = this.resolver.resolveComponentFactory(AlertComponent);
    const ref = this.host.createComponent(factory);
    ref.instance.message = 'Kayıt başarılı';
  }
}

@ViewChild ile template'teki ng-container'a bir ViewContainerRef olarak ulaşıyoruz; burası yeni component'in yerleşeceği yuva. Sonra constructor'dan aldığımız ComponentFactoryResolver ile component için bir factory çözüyor, o factory ile component'i oluşturuyoruz. ref.instance oluşan component'in kendisi, property'lerine doğrudan değer verebiliyoruz.

Angular 13'te aynı iş şöyle:

export class AppComponent {
  @ViewChild('host', { read: ViewContainerRef }) host!: ViewContainerRef;

  showAlert(): void {
    this.host.clear();
    const ref = this.host.createComponent(AlertComponent);
    ref.instance.message = 'Kayıt başarılı';
  }
}

Factory yok, resolver yok, constructor yok. createComponent artık component class'ını doğrudan kabul ediyor. Bu da View Engine'in gidişiyle ilgili: factory'ler eski motorun ihtiyacıydı, Ivy ise gereken her bilgiyi component class'ının üzerinde zaten taşıyor. Factory alan eski imza Angular 13.0'da, ComponentFactoryResolver class'ı da 13.2'de deprecated oldu.

AlertComponent ise her component gibi AppModule'ün declarations listesinde duruyor; NgModule kuralları bu sürümde değişmedi. Eski kodlarda bir entryComponents dizisi görürsen, onu da gönül rahatlığıyla silebilirsin. Ivy ile birlikte zaten gereksiz hâle gelmişti.

Build'ler Neden Hızlandı: Kalıcı Önbellek

CLI 13 ile gelen bir değişikliği büyük ihtimalle ikinci ng serve'de fark edeceksin: başlatma belirgin şekilde daha hızlı. Sebebi proje kökünde oluşan .angular/cache klasörü. CLI build sırasında ürettiği ara sonuçları buraya yazıyor ve bir sonraki build'de değişmeyen kısımları yeniden üretmek yerine oradan okuyor. Hamuru önceden hazırlayıp dondurucuya koymak gibi; her akşam sıfırdan yoğurmak gerekmiyor.

Varsayılan ayarlar angular.json'da yazmasa da şuna eşdeğer:

"cli": {
  "cache": {
    "enabled": true,
    "environment": "local",
    "path": ".angular/cache"
  }
}

"local" değeri, önbelleğin yalnızca kendi makinende açık olduğu, CI ortamında kapalı kaldığı anlamına geliyor. CLI, CI ortam değişkenine bakarak nerede çalıştığını anlıyor. CI'da da önbellekten faydalanmak istersen değeri "all" yaparsın; tamamen kapatmak istersen ng config cli.cache.enabled false yeterli.

Bu klasör git'e girmemeli. Yükseltme sırasında çalışan bir migration, .angular/cache satırını .gitignore dosyana kendisi ekliyor.

Testlerde Sessiz Bir Değişiklik

Daha önce gördüğümüz TestBed tarafında da bir yenilik var. Angular 13'te her testten sonra test module'ü ve oluşturulan component'ler varsayılan olarak yok ediliyor. Bu ayarın adı destroyAfterEach. Faydası şu: bir testin bıraktığı DOM kalıntıları ya da açık kalan subscription'lar bir sonraki teste sızmıyor, her test temiz bir sayfayla başlıyor.

Ama mevcut projelerde bu davranış birden açılırsa, gizlice birbirine yaslanan testler kırılabilir. O yüzden migration, var olan projelerde ayarı bilerek kapalı tutuyor. Yükseltmeden sonra src/test.ts dosyanda şuna benzer bir satır göreceksin:

getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting(),
  { teardown: { destroyAfterEach: false } }
);

Benim önerim, uygun bir zamanda bunu true yapıp ng test çalıştırman. Kırmızıya dönen testler varsa, bunlar zaten bir önceki testin bıraktığı bir şeye güvenen testlerdir. Onları düzeltmek uzun vadede sana kazandırır.

Küçük Bir Deneme

Angular 12 ile oluşturduğun herhangi bir projeyi al; önceki yazılarda ng new ile açtığın bir deneme projesi de olur. Önce her şeyi commit'le, node -v ile Node 16'da olduğunu doğrula ve ng update @angular/core@13 @angular/cli@13 komutunu çalıştır.

Ardından git diff ile şunlara bak: .gitignore'a eklenen .angular/cache satırı, test.ts'teki teardown ayarı ve package.json'da yükselen TypeScript sürümü. Sonra yukarıdaki AlertComponent örneğini projeye ekle; önce Angular 12 tarzında factory ile yaz, sonra factory'siz hâline çevir. Son olarak ng serve'ü iki kez başlatıp ikinci açılışın ne kadar sürdüğüne bak.

Bu yükseltme çoğunlukla kaputun altını temizledi. Sıradaki sürümde değişiklikler daha görünür: Angular 14 ile NgModule'süz component fikrinin ilk hâli olan standalone component'ler, reactive forms'a nihayet gelen tip güvenliği ve constructor'a alternatif olarak inject() fonksiyonu geliyor.