Angular 17 — Deferrable Views

Nahit Ferhat Ektaş
5 min readNov 18, 2023

Angular 17 geçtiğimiz günlerde pek çok yeni özellik ve geliştirmeler ile birlikte yayınlandı. Deferrable Views, Angular 17 ile hayatımıza giren özeliklerden bir tanesi oldu.

Deferrable View ile component’lerimizi veya component’lerimizde yer alan defer bloklarını lazy load olarak yükleyebiliyoruz.

@Component({
selector: 'app-root',
standalone: true,
template: `
<div class="container mx-auto px-4 mt-5">
@defer () {
<app-product [products]="productsList"></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
} @error {
<span>Error</span>
} @loading() {
<app-loading></app-loading>
}
</div>
`,
})
export class AppComponent implements OnInit {
httpClient = inject(HttpClient);
productsList: any;

ngOnInit(): void {
this.httpClient
.get('https://fakestoreapi.com/products')
.subscribe((res) => (this.productsList = res));
}
}
  • @defer: Lazy loading işleminin gerçekleştiği bloktur. Bu blokta yer alan içerikler başlangıçta render edilmez. Sayfada belirlenen koşullar gerçekleştikten sonra yüklenir. Default olarak idle koşulunda yani browser tüm işlemlerini bitirdikten sonra sayfaya yüklenir.
  • @placeholder: defer bloğu tetiklenmeden önce render edilecek bölümdür.
  • @loading: defer bloğu tetiklendiğinde, component’in yüklenmesi sırasında gösterilecek bölümdür.
  • @error: defer bloğu yüklenirken, eğer bir hata dönerse gösterilecek bölümdür.

Şimdi aynı işlemi Deferrable Views kullanmadan gerçekleştirelim.

@Component({
selector: 'app-root',
standalone: true,
template: `
<div class="container mx-auto px-4 mt-5">
<ng-container #products> </ng-container>
</div>
<ng-template #loading>
<app-loading></app-loading>
</ng-template>

<ng-template #placeholder>
<app-placeholder></app-placeholder>
</ng-template>

<ng-template #error>
<h1>Error</h1>
</ng-template>
`,
})
export class AppComponent implements AfterViewInit {
httpClient = inject(HttpClient);

@ViewChild('products', { read: ViewContainerRef })
viewContainerRef!: ViewContainerRef;
@ViewChild('placeholder') placeHolderRef!: TemplateRef<{}>;
@ViewChild('loading') loadingRef!: TemplateRef<{}>;
@ViewChild('error') errorRef!: TemplateRef<{}>;

productList: any;

ngAfterViewInit(): void {
this.viewContainerRef.createEmbeddedView(this.placeHolderRef);

this.httpClient.get('https://fakestoreapi.com/products').subscribe(
(item) => {
this.viewContainerRef.clear();
this.viewContainerRef.createEmbeddedView(this.loadingRef);
import('./product/product.component').then((c) => {
this.viewContainerRef.clear();
const productComponent = this.viewContainerRef.createComponent(
c.ProductComponent
);
productComponent.instance.products = item;
});
},
(error) => {
this.viewContainerRef.clear();
this.viewContainerRef.createEmbeddedView(this.errorRef);
}
);
}
}

Yukarıdaki örnekte görüldüğü gibi, component’lerimizi Deferrable Views öncesinde de lazy load olarak yükleyebiliyorduk ama artık Deferrable Views ile aynı işlemi daha az kod ve daha yüksek performans ile gerçekleştirebiliyoruz.

Deferrable Views kullanmanın 2 koşulu vardır.

  • Component’lar standalone olmalıdır. Standalone olmayan component’ler de defer bloğu eager loading olarak çalışır.
  • Defer blokları, blokların dışında çağırılmamalıdır.

Defer bloğunu tetikleme yöntemleri; on ve when

on: Aşağıdaki listeden bir koşul seçerek, defer bloğumuzu tetikleyebiliriz.

@defer on idle

Internet tarayıcımızın bütün işlemlerini bitirip boşa çıkması ile tetiklenir.

@defer (on idle) {
<app-product></app-product>
}

@defer on viewport

Belirlenen içerik görünen alana girer girmez tetiklenir.

@defer (on viewport) {
<app-product></app-product>
}@placeholder {
<app-placeholder></app-placeholder>
}

Default olarak placeholder bloğunun içerisindeki element yüklendiğinde tetiklenir.

Bunun dışında, template referanslarını da kullanarak defer bloğunu tetikleyebiliriz.

<div #firstElement>First Element</div>
<div class="container mx-auto px-4 mt-5">
@defer (on viewport(firstElement)) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
} @error {
<span>Error</span>
} @loading() {
<app-loading></app-loading>
}
</div>

Yukarıdaki örnekte olduğu gibi, #firstElement yüklendiğinde defer bloğumuz tetiklenecektir.

@defer on interaction

Kullanıcının belirlenen element’e click veya keypress gibi event’leri kullanarak etkileşime girmesi ile tetiklenir.

Eğer tanımlanan bir element yok ise @placeholder ile etkileşime girildiğinde tetiklenir.

@defer (on interaction()) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
}

Bunun dışında, template referansları kullanarak da defer bloğunu tetikleyebiliriz.

template: `
<div class="container mx-auto px-4 mt-5">
<button #product mat-raised-button color="primary">Fetch Data</button>
@defer (on interaction(product)) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
} @error {
<span>Error</span>
} @loading() {
<app-loading></app-loading>
}
</div>
`

Kullanıcı, template varibale olarak tanımladığımız button’ a tıkladığında Angular defer bloğunu render edecektir.

@defer on hover

Kullanıcının on interaction da olduğu gibi belirlenen element’e hover olması durumunda tetiklenir.

Eğer element tanımlanmaz ise, placeholder bloğu default olarak tanımlanmış olur.

@defer (on hover()) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
}

Yine on interaction da olduğu gibi, template referansları kullanarak da defer bloğunu render edebiliriz.

@defer on immediate

Client render olduğunda hemen defer bloğumuz tetiklenir.

@defer (on immediate()) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
}

@defer on timer

İlgili defer bloğunun belirli bir süre sonra yüklenmesini sağlar.

@defer (on timer(5s)) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
}

Yukarıdaki örnekte defer bloğumuz 5 saniye geçtikten sonra tetiklenecektir.

when: Yukarıda tanımlanan listeden bağımsız olarak kendi belirlediğimiz koşullara göre de defer bloğumuzu render edebiliriz. When ile tanımladığımız koşullar bize boolean döner. True olduğunda render işlemi gerçekleşir.

<button (click)="isShow = true" mat-raised-button color="primary">
Show Product
</button>
@defer (when isShow) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
} @error {
<span>Error</span>
} @loading() {
<app-loading></app-loading>
}

Yukarıdaki örnekte, isShow başlangıçta false durumunda olduğu için defer bloğu render edilmiyor. Button’a tıkladığımızda isShow = true olduğu için defer bloğundaki component render edilmiş oluyor.

Defer bloklarımıza birden fazla trigger(tetikleyici) tanımlayabiliriz.

<button (click)="isShow = true" mat-raised-button color="primary">
Show Product
</button>
@defer (on interaction; on timer(5s); when isShow) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
} @error {
<span>Error</span>
} @loading() {
<app-loading></app-loading>
}

Yukarıdaki örnekte 3 farkı trigger(tetikleyici) tanımladık. app-product component’i sayfada hiç bir aksiyon olmazsa 5 saniye sonunda render edilecek. Eğer button veya placeholder bloğu ile etkileşim gerçekleşirse, 5 saniye beklemeden defer bloğu tetiklenecek.

Birden fazla trigger(tetikleyici) kullanıldığında, Angular defer bloğunu render etmek için bütün koşulların gerçekleşmesini değil yalnızca bir koşulun gerçekleşmesini bekleyecektir.

Prefetch

Defer bloklarındaki içeriklerimizi prefetch(ön yükleme)olarak yükleyebiliyoruz. Yani kullanıcı sayfada defer bloğu render edilmeden önce, render edilecek içerikleri ön yükleme ile daha öncesinde almış oluyor. Bu da ihtiyaç anında, ilgili içeriğin daha hızlı yüklenmesini sağlıyor.

<button #product mat-raised-button color="primary mr-2">
Show Product
</button>
<button #prefetch mat-raised-button color="primary">Prefetch</button>
@defer (on interaction(product); prefetch on hover(prefetch)) {
<app-product></app-product>
} @placeholder {
<app-placeholder></app-placeholder>
} @error {
<span>Error</span>
} @loading() {
<app-loading></app-loading>
}

Yukarıdaki örnekte defer bloğunun içinde “(on interaction(product); prefetch on hover(prefetch))”

Bu da şu demek oluyor, kullanıcı “Prefetch” button’unun üzerine(hover) geldiği zaman defer bloğunu prefetch(ön yükleme) olarak yükle. Daha sonrasında Show Product button’una bastığında ise sayfayı render et.

Bu yazımda Angular 17 ile gelen Deferrable Views yapısını anlatmaya çalıştım. Umarım faydalı olmuştur. Görüşmek üzere.

--

--