20 sie 2025
6 min

Angular 20.2 – ostatnie zmiany

Angular właśnie doczekał się aktualizacji, a wraz z nią pojawiło się sporo nowych funkcjonalności wartych uwagi. Przyjrzyjmy się im razem.

Zmiana statusu Zoneless Angulara-  jest teraz stabilny!

Czekaliśmy na to od dawna i w końcu się doczekaliśmy. W najnowszej wersji Angulara tryb Zoneless jest już stabilny, więc śmiało można go używać w swoich projektach. Temat Zoneless w Angularze jest jednak dość obszerny, dlatego w tym artykule skupimy się na innych, świeżych zmianach. Jeśli chcesz dowiedzieć się, jak to działa, kliknij tutaj.

Lepsze wsparcie dla TypeScript (5.9).

W najnowszej wersji Angulara możemy w pełni korzystać z możliwości TypeScript 5.9, który jest teraz w pełni wspierany. Otwiera to drzwi do wielu nowoczesnych usprawnień języka, które mogą poprawić zarówno wydajność, jak i komfort pracy programistów.

Na przykład, teraz możliwe jest zaimportowanie modułu bez natychmiastowego wywołania jego wykonania:

import defer * as myFeature from "./common-module.js"
//no side effect//

//Here code is executed//
const value = myFeature.count();

Warto również wspomnieć, że TypeScript 5.9 wprowadza niewielkie, ale istotne usprawnienie — możliwość podglądu pełnych typów interfejsów w podpowiedziach edytora oraz podczas inspekcji po najechaniu kursorem. To ulepszenie znacząco poprawia komfort pracy programisty, ułatwiając zrozumienie złożonych typów bez konieczności przeskakiwania między wieloma definicjami. Spójrz na poniższy przykład:

Więcej informacji o nowej wersji TypeScript można znaleźć tutaj.

Ulepszone wsparcie dla właściwości redirect.

Na początek wyjaśnijmy, w czym ta zmiana może nam pomóc. Przede wszystkim umożliwia ona dodanie własnej logiki opartej na tym czy żądanie zostało przekierowane bądź też nie. Zobacz poniższy przykład:

export class RedirectServiceExample {
  private readonly _httpClient = inject(HttpClient);
  private readonly _userHandler = inject(UserHandler);
 
  getUser(Id: string) {
    return this._httpClient.get(`/api/users/${Id}`, { observe: 'response'     }).pipe(
      map((response: HttpResponse<any>) => {
        if (response.redirected) 
          return this._userHandler.processRedirectedUser(id)
        else return this._userHandler.processUser(response.body)
      })
    );
  }
}

Widzimy, że nasza logika jest dodawana warunkowo, co daje nam pełną kontrolę nad tym, jak w tej sytuacji obsługiwane są żądania. Oznacza to, że w zależności od tego, czy odpowiedź została przekierowana, możemy zastosować różne strategie obsługi –  na przykład rejestrowanie przekierowania do celów analitycznych, wdrożenie dodatkowych kontroli bezpieczeństwa lub modyfikację przebiegu działania aplikacji.

Po drugie, zmiana ta poprawia spójność z natywnym Fetch API. Dzięki udostępnieniu właściwości redirected obiekt HttpClient zachowuje się teraz bardziej jak Fetch API, oferując programistom znajomy i przewidywalny interfejs.

Co więcej, nowa funkcjonalność pozwala lepiej zadbać o bezpieczeństwo naszych aplikacji. Prosty przykład: możemy blokować nieautoryzowane przekierowania lub wprowadzać dodatkową weryfikację w momencie, gdy w aplikacji wykryte zostanie przekierowanie. Jest to bez wątpienia przydatne w kontekście polityk między domenowych (cross-origin).

Dane analityczne w kontekście właściwości redirected są istotne, ponieważ pozwalają śledzić, jak często i w jakich okolicznościach następuje przekierowanie w aplikacji. Pomaga to ocenić wydajność, stabilność oraz zgodność przebiegu żądań z zamierzoną architekturą.

Nowe API animacji

Dzięki nowemu API animacji możemy uniknąć importowania modułu animacji, co ma istotny wpływ na naszą aplikację. Przede wszystkim może to zmniejszyć ostateczny rozmiar aplikacji. Ponadto łatwiej jest obsługiwać animacje, korzystając z czystych plików stylów, a nie z bibliotek specyficznych dla Angulara.

Kolejną zmianą wartą uwagi jest wprowadzenie nowych funkcjonalności: leave i enter, które pozwalają animować elementy HTML bez konieczności importowania modułu animacji (jak wspomniano wyżej). Takie podejście ułatwia także tworzenie animacji osobom, które dopiero zaczynają swoją przygodę z Angularem. Zobacz, jak działa to w najnowszej aktualizacji:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {
  private readonly _menuOpen = signal(false);
  protected readonly isMenuOpen = computed(() => this._menuOpen());


  protected toggleMenu() {
    this._menuOpen.update(value => !value);
  }


  protected closeMenu() {
    this._menuOpen.set(false);
  }
}

Plik html:

<button (click)="toggleMenu()">Toggle Menu</button>


@if (isMenuOpen()) {
  <div
    animate.enter="animate-in"
    animate.leave="animate-out"
    (click)="closeMenu()"
  >
    <!-- Your content -->
    <p>Click to close</p>
  </div>
}

I finalnie nasze style:

@keyframes animate-in {
  from {
    transform: translateY(50%);
  }
  to {
    transform: translateY(0);
  }
}


.animate-in {
  animation: animate-in 0.35s ease-in-out;
}


@keyframes animate-out {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(50%);
  }
}


.animate-out {
  animation: animate-out 0.35s ease-in-out;
}


Moim zdaniem jest to lepsze rozwiązanie pod kątem doświadczenia programisty. Zobacz ten przykład z użyciem animationModules:
<h2>Old Angular Animations</h2>
<button (click)="toggleMenu()">Toggle Menu</button>


@if (isMenuOpen()) {
  <div
    @menu
    (click)="closeMenu()"
  >
    <p>Click to close</p>
  </div>
}



@Component({
 /…/,
 animations: [
    trigger('menu', [
      transition(':enter', [
        style({ transform: 'translateY(120%)' }),
        animate('0.35s ease-in-out', style({ transform: 'translateY(0)' }))
      ]),
      transition(':leave', [
        animate('0.35s ease-in-out', style({ transform: 'translateY(120%)' }))
      ])
    ])
})
export class AppComponent {
  private readonly _menuOpen = signal(false);
  protected readonly isMenuOpen = computed(() => this._menuOpen());


  protected toggleMenu() {
    this._menuOpen.update(value => !value);
  }


  protected closeMenu() {
    this._menuOpen.set(false);
  }
}

Abstrahując od zastosowanych stylów inline, wyraźnie widać, że nowe funkcjonalności są przyjemniejsze w użyciu. Co więcej, dzięki najnowszym zmianom wydajność może ulec poprawie w przypadku aplikacji działających np. na urządzeniach mobilnych. Jak wiemy, niektóre animacje z biblioteki są wykonywane przy użyciu CPU zamiast GPU, co może negatywnie wpływać na wydajność aplikacji.

Większe możliwości stosowania @else if

Wraz z wprowadzeniem nowej wersji Angulara możemy używać słowa kluczowego as, aby wyodrębnić interesujące nas pola obiektu i używać ich bezpośrednio w templatce HTML. Teraz dotyczy to warunku @else if. Spójrz –  to naprawdę proste:

@Component({
 //…//
 template: `
    @if(user(); as user) {
      <p>{{user.name}}</p>
    } @else if(admin(); as admin) {
      <p>Hi admin {{admin.nickName}} </p>
    }
 `,
})
export class App {
  //…//
})

W tym bardzo prostym przypadku możemy po prostu odczytać typ użytkownika i, korzystając z zawężania typów, warunkowo wyświetlać różną zawartość w szablonie.

Rozszerzona diagnostyka dla niewywołanej funkcji

Przed wydaniem Angulara 20.2 nie było dodatkowego sprawdzenia w sytuacji, gdy funkcja była umieszczona wewnątrz interpolacji tekstu. Wcześniej taki mechanizm ostrzegania został zaimplementowany dla sygnałów:

@Component({
 template: `
   {{text}}
 `,
})
export class App {
 protected readonly text = signal<string>('Hello angular 20.2');
}

W takiej sytuacji następujący błąd jest wyświetlany:

[WARNING] NG8109: text is a function and should be invoked: text() [plugin angular-compiler]

Podobnie, wraz z wprowadzeniem Angulara 20.2 błąd jest wyświetlany gdy funkcja nie jest wywołana:

@Component({
  //…//
  template: `
    {{text}}
 `,
})
export class App {
  protected text(): string {
   return ‘Hello Angular 20.2’;
  }
}

Jednak mam nadzieję, że unikasz bezpośredniego wywoływania funkcji w szablonach. To dobrze znany antywzorzec w Angularze, który może prowadzić do niepotrzebnych cykli detekcji zmian i pogorszenia wydajności aplikacji. Zamiast tego warto rozważyć użycie getterów lub wcześniej obliczonych właściwości w klasie komponentu.

Zastąpienie metody getCurrentNavigation

Od Angulara 20.2 reaktywność naszej aplikacji nieco wzrosła. Teraz możemy wywołać currentNavigation() z instancji Router i obliczyć jej wartość, zamiast korzystać z metody getCurrentNavigation, która zwraca obiekt Navigation lub null. currentNavigation jest sygnałem, więc możemy wyciągnąć jego stan z użyciem computed. Pamiętaj, że od najnowszej aktualizacji metoda getCurrentNavigation jest przestarzała (deprecated).

@Component({
  //…//
})
export class App {
  private readonly _router = inject(Router);
  protected readonly navigated = computed(() =>
    this._router.currentNavigation()
  );
}

Zmodyfikowane API FormArray.

Od wydania Angular 20.2 API FormArray zostało zmienione. Wcześniej, jeśli chciałeś dodać tablicę kontrolerów do FormArray, musiałeś iterować po tablicy i w każdej iteracji dodawać element do FormArray:

export class App implements OnInit {
 protected formArray = new FormArray<FormControl<string | null>>([]);
 private readonly _controls = [
   new FormControl({ value: '', disabled: false }),
   new FormControl({ value: 'Name', disabled: false }),
 ];


 ngOnInit() {
   // before Angular 20.2 release
   this._controls.forEach((control) => {
     this.formArray.push(control);
   })
 }

Obecnie możesz modyfikować formArray w ten sposób:

 ngOnInit() {
   // after Angular 20.2 release
   this.formArray.push(this._controls);
 }

Gdy dodajesz elementy do FormArray w pętli, przy każdej iteracji wyzwalane jest osobne zdarzenie. W nowej metodzie zdarzenie jest wyzwalane tylko raz – po dodaniu wszystkich kontrolerów – co poprawia wydajność Angulara.

Udostępnienie opcji referrer i integrity dla httpResource

Z każdą nową wersją Angulara (zob. artykuł o Angular 20.1) funkcjonalności httpResource były stopniowo rozwijane, a w najnowszej wersji została rozszerzone o opcje referrer i integrity. Warto wspomnieć, dlaczego te możliwości są potrzebne i jak można je wykorzystać.

Dzięki opcji referrer możemy określić lub ukryć stronę źródłową, z której wykonywane jest zewnętrzne żądanie HTTP. Natomiast opcja integrity pozwala sprawdzić, czy dany zasób nie został zmodyfikowany – co ma istotny wpływ na bezpieczeństwo naszych aplikacji.

Poniżej znajduje się prosty przykład.

@Component({
  //…//
})
export class App {
   protected readonly resource = httpResource(() => ({
   //rest of settings//
   referrer: 'no-referrer',
   integrity: 'your_sha_key',
 }));
};

Dozwolone wiązanie z atrybutem ARIA.

Od najnowszej wersji Angulara możemy bezpośrednio wiązać atrybuty ARIA bez konieczności używania przedrostka attr.. Zmiana ta została wprowadzona nie tylko w celu uproszczenia pracy programistów, ale przede wszystkim w celu poprawy renderowania po stronie serwera.

Poniżej znajduje się przykład:

@Component({
 //…//
 template: `
   <button [ariaLabel]=”label”>Click me</button> //New way
   <button [attr.aria.label]=”label”>Click me</button> //Old way
 `,
})
export class App {
 protected readonly label = `New aria property binding`;
}

Podsumowanie

Moim zdaniem powinieneś rozważyć aktualizację wersji Angular w swoim projekcie do wersji 20.2. Ostatnie zmiany wprowadziły wiele usprawnień, które wpływają zarówno na wydajność aplikacji, jak i na komfort pracy dewelopera, co z pewnością warto odnotować. Aby zobaczyć, co zostało zmienione w poprzednich wersjach Angular, zachęcam do przeczytania tego artykułu:

Podziel się artykułem

Zapisz się na nasz newsletter

Dołącz do community Angular.love i bądź na bieżąco z trendami.