3
votes

I have a custom structural directive in my angular app like this:

@Directive({
  selector: '[appIfData]'
})
export class IfDataDirective {

  private hasView = false;

  @Input()
  set appIfData(condition: boolean) {
    if (condition && !this.hasView) {
      this.viewContainerRef.clear();
      this.viewContainerRef.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (!condition) {
      this.viewContainerRef.clear();
      const factory = this.componentFactoryResolver.resolveComponentFactory(ContentMessageComponent);
      const messageComponentRef = this.viewContainerRef.createComponent(factory);
      messageComponentRef.instance.message = 'No data is available yet';
      messageComponentRef.instance.icon = 'fas fa-info';
      this.hasView = false;
    }
  }
}

Using it in the html template :

<ng-container *appIfData="(referenceService.documentUserTypes$ | async) as userDocTypes">

But I can't access the declared variable 'userDocTypes' in the rest of the template like i was doing when using ngIf for example.

I guess it's a normal behavior but I can't find a good way to do this.

Any help would be greatly appreciated.

EDIT :

this is how i am using it, it is in a child element. As said before it works fine if I just change it to *ngIf :

enter image description here

EDIT 2 :

Updated directive

@Directive({
  selector: '[appIfData]'
})
export class IfDataDirective {

  private hasView = false;

  @Input()
  set appIfData(data: any) {
    if (data && !this.hasView) {
      this.viewContainerRef.clear();
      this.viewContainerRef.createEmbeddedView(this.templateRef, { appIfData: data });
      this.hasView = true;
    } else if (!data) {
      this.viewContainerRef.clear();
      const factory = this.componentFactoryResolver.resolveComponentFactory(ContentMessageComponent);
      const messageComponentRef = this.viewContainerRef.createComponent(factory);
      messageComponentRef.instance.message = 'No data is available yet';
      messageComponentRef.instance.icon = 'fas fa-info';
      this.hasView = false;
    }
  }
3
could you please provide an example of where would you like to use userDocTypesAndrei
I have edited the questionTheo

3 Answers

3
votes

the variable which declared in as is only accessible within its child element like

<ng-container *appIfData="(referenceService.documentUserTypes$ | async) as userDocTypes">
     <div>{{ userDocTypes }}</div>  // you can access here
</ng-container>

<div>{{ userDocTypes }}</div> // you cannot access here

I think you got confused with template reference which can be accessed with in its template

<div>
    <input type="text" #myInput value="123">
</div>

<div>{{ myInput.value }}</div> // you can access template variable outside of it
2
votes

After looking a t Andrei comment in his answer, I was using this :

 /**
     * Cache for document user types
     */
    public documentUserTypesCache$: BehaviorSubject<DocumentUserType[]>;

    /**
     * Get document user types
     */
    public get documentUserTypes$(): BehaviorSubject<DocumentUserType[]> {
        if (!this.documentUserTypesCache$) {
            this.documentUserTypesCache$ = new BehaviorSubject<DocumentUserType[]>([]);
            this.getDocumentUserTypes().pipe(tap(r => this.documentUserTypesCache$.next(r))).subscribe();
        }
        return this.documentUserTypesCache$;
    }

and changed it to this:

/**
 * Cache for document user types
 */
public documentUserTypesCache$: BehaviorSubject<DocumentUserType[]>;

/**
 * Get document user types
 */
public get documentUserTypes(): DocumentUserType[] {
    if (!this.documentUserTypesCache$) {
        this.documentUserTypesCache$ = new BehaviorSubject<DocumentUserType[]>(null);
        this.getDocumentUserTypes().pipe(
            filter(r => r && r != null),
            tap(r => this.documentUserTypesCache$.next(r))).subscribe();
    }
    return this.documentUserTypesCache$.getValue();
}

not the best but the workaround works

1
votes

you could add context to createEmbeddedView like this

this.viewContainerRef.createEmbeddedView(this.templateRef, {appIfData: condition});

also note, that condition is not boolean in your examples, it can crash during aot compilation. change condition type to something else