skip to Main Content

enable.service.ts

@Injectable({
  providedIn: 'root'
})
export class EnableService {
  isEnabled$ = from(this.client.init()).pipe(
    switchMap(() => this.client.getEnabled()),
    map(([enabled, isAdmin]) => ({enabled: true, isAdmin: false})),
    share()
  ); // not exactly like this but some kind of observable that will return enable and isAdmin

  constructor(
    // some constructor
  ) {}
}

main.component.ts

export class MainComponent implements OnInit, AfterViewInit, OnDestroy {
  @ViewChild('mymap') containerRef: ElementRef<HTMLDivElement>;

  isAdmin: boolean = false;
  isEnable: boolean = false;

  constructor(
    private enableService: EnableService,
  ) {
    this.enableService.isEnabled$.subscribe(res => {this.enable = res.enabled; this.isAdmin = res.isAdmin});
  }

  async ngAfterViewInit(): Promise<void> {
    // HACK: need to wait the enableService subscription to get the actual value, otherwise containerRef #mymap is not known whether it is exist or not
    await new Promise(resolve => setTimeout(resolve, 1000));

    // have to exist so we can hide/remove the map properly
    if (this.containerRef) {
      // some third party sdk initialization should only get executed if containerRef exist
    }
  }

main.component.html

<div #mymap *ngIf="!isEnable || isAdmin"></div>

right now this is a working code, with some hack of 1 second delay inside ngAfterViewInit. The problem is what happened if the enableService take half a second longer, it will break.

Is there anyway to make sure ngAfterViewInit got executed after enableService retrieve the value?

or is there a better way so I don’t have to use that 1 second delay. If I set the initial value of isEnable to false, and then it ended up true, it will cause an issue, likewise if it’s true and ended up true, it will cause error too. I tried moving the observable subscription from constructor to ngOnInit, it also does not work.

2

Answers


  1. Does this work?

    Create a child component inside the NgIf, so that it will trigger the ngafterviewinit after the *ngIf becomes true!

    ts

    get isEnabled$() {
        return this.enableService.isEnabled$;
    }
    

    html

    <div #mymap *ngIf="!(isEnabled$ | async).isEnable || (isEnabled$ | async).isAdmin">
        <child-component></child-component>
    </div>
    
    Login or Signup to reply.
  2. You can add a setter for your containerRef:

    private _containerRef: ElementRef<HTMLDivElement>; 
    
    @ViewChild('mymap') set content(content: ElementRef) {
      this._containerRef = content;
      if (content) {            
        // Do whatever you need with the ref
       }
    }
    

    In the setter function you will get all the changes if an element appears, if disappears and you do not have to sync manually with a service call.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search