How can I use async pipe with @defer to load and declare variable in template in Angular 17
Asked Answered
S

3

5

For Example using @if, I am able to do this: @if(items$ | async; as items). I am able to declare 'items' as a variable holding the array of items using the 'as' keyword in the template.

How can I achieve the same thing using @defer? Something like: @defer(when items$ | async; as items)

I tried @defer(when items$ | async; as items) but angular template does not like it.

Summerlin answered 14/11, 2023 at 0:18 Comment(2)
at the moment it looks like this syntax is not supported - "when specifies an imperative condition as an expression that returns a boolean" - angular.dev/guide/defer#triggers though I did get when items$ | async without the variable working. Consider opening a feature request on angular githubRaindrop
Okay, thanks, I'll add as a feature request.Summerlin
L
6

@defer blocks don't allow the creation of aliases, only @if blocks allow that.

You should simply wrap your @defer with an @if block.

@if(items$ | async; as items) {
   @defer() {
      ...
   }
} else {
  ...
}
Lefton answered 14/11, 2023 at 9:15 Comment(1)
Yeah, I had to go that route, but I really liked the 'placeholder' block that comes with 'defer', which allows you to specify how long to show a loading element before showing the actual content. If only I could load a placeholder while waiting on data with a timer before displaying content on the template. Probably not part of this topic, but if you have a way I could do it, that will be cool, but for now I'll add a feature request to see if angular can add this. Thanks!Summerlin
M
2

Deferred view work perfectly well with observables or promises, but your as items is not a valid option for the @defer control flow. You'll have to move that to a @for block instead.

Misprision answered 14/11, 2023 at 9:7 Comment(0)
R
1

To get placeholder behaviour,

Stackblitz

import 'zone.js';
import { Component, computed } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AsyncPipe } from '@angular/common';
import { of, delay } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [AsyncPipe],
  template: `

    @defer (when loaded()) {
      <h1>DEFERRED</h1>
      @for(item of items(); track item) {
        <div>{{item}}</div>
      }
    } @placeholder {
      <p>PLACEHOLDER</p>
    }
  `,
})
export class App {
  name = 'Angular';
  items$ = of([1, 2, 3, 4]).pipe(delay(3000));
  items = toSignal(this.items$);

  loaded = computed(() => {
    return !!this.items()?.length;
  });
}

bootstrapApplication(App);
Raindrop answered 14/11, 2023 at 18:56 Comment(1)
@defer (when items()) works alsoRaindrop

© 2022 - 2024 — McMap. All rights reserved.