Search code examples
javascripthtmlangulartypescriptangular13

Angular - How to get an element by id inside ngOnInit


I want to get the below checkbox element in ts to manipulate its value (checked = true)

<div class="input-wrapper" *ngFor="let i of rolesData">
    <input type="checkbox" name="{{i.role}}" id="{{i.role}}" class="selectedRole">
    <label for="{{i.role}}">{{i.role}}</label>
</div>

i.role is 'Software Developer'. Now the following code inside ngOnInit prints null.

console.log("element", document.getElementById('Software Developer'))

Please help.


Solution

  • The reason why it prints undefined as ngOnInit is triggered when Angular initializes a component or directive.

    For your scenario, you need ngAfterViewInit which this event is triggered after Angular renders the view. So that you can get the element from the view.

    ngAfterViewInit() {
      console.log('element', document.getElementById('Software Developer'));
    }
    

    Reference: Responding to view changes


    Updated

    According to your requirement that the roleData is fetched from Observable which is asynchronous. Meanwhile the ngAfterViewInit is the event that is triggered when Angular initializes the view but not when the view is completed.

    Hence, to get the element from the view, it must be fulfilled with:

    1. The observable is returned.
    2. The HTML element is rendered.

    For the implementation as the code below:

    1. With isDataFetchedSubject Subject, when it is completed, indicate that the roleData observable is returned (isDataFetched$).

    2. With isViewRenderedSubject Subject, when it is completed, indicate that the view is rendered. And this requires the ngAfterViewChecked lifecycle which is triggered when each time the view is rendered/changed (isViewRendered$).

    3. Requires the observables mentioned in 1 and 2 must be completed.

    4. With the setTimeout function to delay the function execution, continuously get the element from the view until the element is rendered.

    isDataFetchedSubject: Subject<boolean> = new Subject<boolean>();
    isDataFetched$ = this.isDataFetchedSubject.asObservable();
    isViewRenderedSubject: Subject<boolean> = new Subject<boolean>();
    isViewRendered$ = this.isViewRenderedSubject.asObservable();
    
    ngOnInit() {
      this.rolesData$.subscribe((value: any[]) => {
        this.rolesData = value;
    
        this.isDataFetchedSubject.next(true);
        this.isDataFetchedSubject.complete();
      });
    
      forkJoin([this.isDataFetched$, this.isViewRendered$])
        .pipe(takeUntil(this.destroyed$))
        .subscribe(([flag1, flag2]) => {
          console.log('Observable is completed');
    
          if (flag1 && flag2) {
            // Create a delay function to fetch element until it is existed
            let checkElementExist = setTimeout((isExisted: boolean) => {
              if (isExisted) clearTimeout(checkElementExist);
    
              console.log(
                '(2) element',
                document.getElementById('Software Developer')
              );
            });
          }
        });
    }
    
    ngAfterViewChecked() {
      this.isViewRenderedSubject.next(true);
      this.isViewRenderedSubject.complete();
    }
    

    Demo @ StackBlitz