25

I have the following in my component template:

<div *ngIf="user$ | async as user>...</div>

Within the above div I would like to use the async pipe to subscribe to another observable only once, and use it just like user above throughout the template. So for instance, would something like this be possible:

<ng-template *ngIf="language$ | async as language>
<div *ngIf=" user$ | async as user>
  <p>All template code that would use both {{user}} and {{language}} would go in between</p>
  </div>
</ng-template>

Or can this even be combined in one statement?

Sammy
  • 3,395
  • 7
  • 49
  • 95
  • I think you're asking if you can introduce "user" as a new template variable, and the answer is no. The async pipe just yields the value as part of the expression. You would have to use the async pipe everywhere in the template you want to use the user value. – Reactgular Jul 01 '17 at 03:11
  • All the logic I need is within the `div` above. I therefore do not need to reintroduce `user` as a variable anywhere else, since I already have it. – Sammy Jul 01 '17 at 11:24
  • Please add more code that will explain what you are trying to do – yurzui Jul 04 '17 at 09:14
  • https://plnkr.co/edit/xiOeJ5pbz2jmx8TIx4c0?p=preview – yurzui Jul 04 '17 at 09:29
  • 1
    Right, my only issue here is that I'm using an `*ngIf` for a value that I know for a fact exists, which is in this case the language observable. – Sammy Jul 04 '17 at 09:31
  • Please reproduce it in plunker – yurzui Jul 04 '17 at 09:41
  • I don't know how, hence my question lol -- is it possible to yield the `language$` observable with the `async` pipe without using `*ngIf`? – Sammy Jul 04 '17 at 09:42
  • https://stackoverflow.com/questions/38582293/how-to-declare-a-variable-in-a-template-in-angular2/43172992#43172992 Anyway you have to use structural directive for that – yurzui Jul 04 '17 at 09:54
  • *That's* what I was looking for, thank you! Am I blind or does this exist nowhere in the docs? – Sammy Jul 04 '17 at 10:02

3 Answers3

27

You can use object as variable:

<div *ngIf="{ language: language$ | async, user: user$ | async } as userLanguage">
    <b>{{userLanguage.language}}</b> and <b>{{userLanguage.user}}</b>
</div>

Plunker Example

See also

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • One more question please: any performance hinderance to this method vs. adding an extra `ng-container` wrapper as you've suggested in your plunkr? – Sammy Jul 04 '17 at 10:04
  • There is no much difference between `
    ` and `
    ` but `ng-container` will generate more code that angular should handle
    – yurzui Jul 04 '17 at 10:11
  • @Sammy We usually use `ng-container` if we have more than one structural directive on element – yurzui Jul 04 '17 at 10:12
  • @Sammy You can compare it here https://plnkr.co/edit/7Yd0IWEmMdQolKHf6bhw?p=preview – yurzui Jul 04 '17 at 10:19
  • 8
    Although this does subscribe to both observables at once, the solution is misleading as the resulting object is always truthy regardless of the state of the observables e.g. `{ language: undefined, user: undefined }`. Using `{{ userLanguage.user.id }}` would result in an error. – Frayt Feb 15 '19 at 08:42
  • @Frayt raises a pretty valid point here - it kinda goes against the whole point of the *ngIf in the first place – jarodsmk Mar 31 '20 at 12:29
13

The problem with using "object as variable" is that it doesn't have the same behavior as the code in the question (plus it's a mild abuse of *ngIf to have it always evaluate to true). To get the desired behavior you need:

<div *ngIf="{ language: language$ | async, user: user$ | async } as userLanguage">
   <ng-container *ngIf="userLanguage.language && userLanguage.user"> 
      <b>{{userLanguage.language}}</b> and <b>{{userLanguage.user}}</b>
   </ng-container>
</div>
kayjtea
  • 2,979
  • 1
  • 20
  • 19
3

While the other solutions work, they slightly abuse the purpose of ngIf which should only optionally render a template. I've written an ngxInit directive that always renders even if the expression result is "falsy".

<div *ngxInit="{ language: language$ | async, user: user$ | async } as userLanguage">
   <!-- content -->
</div>

see https://github.com/amitport/ngx-init

Amit Portnoy
  • 5,957
  • 2
  • 29
  • 30