Angular custom ngIf directive 'as' syntax
Asked Answered
P

2

11

I'm creating a custom *ngIf directive to replace content with a placeholder while it's loading. I have everything working as I want and modeled it after the *ngIf directive (https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts) The only thing not working is the 'as' syntax and I don't see any references to it or where to start.

*myCustomDirective="loading$ | async as result" 

The above does not work, result is undefined when the loading$ observable emits data. The placeholder is shown and replaced with the content as expected however. (Content is giving errors though because of the undefined result)

Placida answered 1/3, 2019 at 16:35 Comment(2)
Try "(loading$ | async) as result".Jamikajamil
Still doesn't work.Placida
L
16

Updated answer: You can copy and paste NgIf directive from Angular github and the only thing you have to do is to change name of NgIfContext#ngIf to match your custom if directive name, for example:

export class NgIfContext {
  public $implicit: any = null;
  public appCustomIf: any = null;
}

Then change names of all Input() respectively to also match your directive name. With this, as keyword will work. See StackBlitz demo.

EDIT: As a additional reference you can follow these commits in #15025 pull request to see how they implemented as keyword in NgIf and NgForOf. In short:

  1. In a63d57f they added as keyword to parser.
  2. In 203ee65 and ef93998 they converted NgIf and NgForOf respectively to use as syntax.
  3. Finally in 71d43db they exposed NgForOfContext,NgIfContext and made NgForOf to use NgForOfContext.

Also if you are curious you can see how they implemented let in NgIf by following #13297 pull request.

This can help you understand what is going on underneath.


Original answer: If you don't really care about as syntax and you only need working template variable this will do the job.

*appCustomIf="test$ | async; let result"

Works with copied and pasted ngIf from Angular github. See StackBlitz demo.

Logotype answered 4/3, 2019 at 21:2 Comment(1)
Can I know what I should improve because someone downvoted my answer? (meaning it's not useful)Logotype
M
4

It's about microsyntax of Angular structural directives (https://angular.io/guide/structural-directives#microsyntax)

If you want you context to be exported through as syntax you should use exactly the same name which your directive has when exporting it, i.e.

this.viewContainerRef.createEmbeddedView(this.templateRef, {myCustomDirective: this.context})

You can also use $implicit syntax - this will allow you to export context through let variable without naming it (default export).

See this example for the details.

Mendicity answered 4/3, 2019 at 21:31 Comment(5)
After some more testing it revealed that I had to make a custom NgIfContext class like the accepted answer, otherwise the component wasn't updating if subscribed to an observable.Placida
It's strange because I've tested both variants - with async pipe and without it - you can check, I've updated my stackblitz example. Could you provide me with some details what actually didn't work?Mendicity
Here is a sample with *ngIf, your solution and the accepted answer solution. You can see there is some problems with your solution. stackblitz.com/edit/angular-gjqg1pPlacida
It's not because of NgIfContext. I'm just not clearing container before rerendering. See the update exampleMendicity
My mistake, yes. Looking at my change sets all I did was replace {myCustomDirective: this.context} with the NgIfContext. Previously the content wasn't getting updated when the observable emitted a new value and after the content was being updated correctly. Unfortunately I don't have time to try reproduce everything, but some edge case was causing it not to work.Placida

© 2022 - 2024 — McMap. All rights reserved.