0

I'm in a case where I want to display observable data in an element evaluated with ngIf.

<div *ngIf="contition$ | async">
    <element> {{(data$ | async)}} </element>
</div>

As you know, observable needs a subscriber, otherwise it will never be called.

My problem is that my observable depends on an event executed after the validation of the ngIf condition. As a result, my element does not display anything.

How to make my observable subscribed, before my ngIf is triggered?

Edit: My problem is the following, I want to display data$. As you can see, to subscribe to data$, my element must wait for the observable contition$. But data$ is an observable that depends on other upstream events, which necessarily come before contition$ itself depending on its events.

sushiLover
  • 56
  • 2
  • 8

3 Answers3

1

It's hard to properly advise without the details of contition$ and data$ but one way is to create a view model stream for the template:

interface IComponentViewModel {
  showData: boolean
  data: string // your data interface
}

@Component(...)
export class YourComponent {

  vm$: Observable<IComponentViewModel>;

  constructor() {
    this.vm$ = combineLatest([contition$, data$]).pipe(
      map(([contition, data]) => {
        return {
          showData: contition,
          data
        }
      })
    )
  }

}

And then use in your template like so:

<div *ngIf="vm$ | async as vm">
  <div *ngIf="vm.showData">
    <element> {{ vm.data }} </element>
  </div>
</div>
Andrew Allen
  • 6,512
  • 5
  • 30
  • 73
  • 1
    Sorry the code I'm working on is quite complicated, I preferred to simplify. Your solution works in any case – sushiLover Mar 01 '23 at 12:52
0

My problem is that my observable depends on an event executed after the validation of the ngIf condition

The ngIf is not triggered only once, this is a wrong assumption.

You could replace the value of the ngIf with a variable from your component's TS file, then every time you would change that variable's value, the ngIf will notice and will remove / add its content to the DOM.

Thus your problem lies somewhere else.

How to make my observable subscribed, before my ngIf is triggered?

That would indeed solve the problem, but I don't know how to achieve that. The trick you could do is to emit the event AFTER the ngIf subscribes.

What you could do is use a BehaviorSubject as your observable. This will keep emitting the last event to every new subscriber. So if you originally emit the event at T0 and you subscribe at T1, the behaviorSubject will dispatch the old event to the new subscriber. You should find plenty of info on google about BehaviorSubject, its purpose is exactly your scenario.

EDIT: also check this post

Mihai
  • 100
  • 13
  • I'm not sure I understand what you mean by, ngIf is not triggered only once. Doesn't the BehaviorSubject also need a subscriber? – sushiLover Mar 01 '23 at 12:22
  • The BehaviorSubject needs a subscriber as well, correct. That subscriber will be the ngIf. What I mean by "not triggered only once" is that if the observable (to which ngIf is subscribed to) emits multiple values, then the ngIf will take into consideration those values (thus not being triggered only once). – Mihai Mar 01 '23 at 12:37
0

After having the review from my technical manager, here is the solution he proposed:

    <ng-container *ngIf="{data: data$ | async, data2: data2$ | async, ...etc} as vm">
      <!-- some code -->
        <div *ngIf="contition$ | async">
           <element> {{(vm.data)}} </element>
        </div>
      <!-- some code -->
    </ng-container>

Asynchronous data that must be retrieved at the beginning of the component loading are stored in an object called vm. vm being an object it always returns true at ngIf*.

sushiLover
  • 56
  • 2
  • 8