Search code examples
javascriptangulartypescriptcordovaionic-framework

How to correctly wait the HTML checkbox element to load before programatically checking it?


I want to dynamically activate some checkboxes when the user enters the page. For this I am saving the IDs of the HTML checkbox elements in a service. When the user enters the page, this service is accessed and returns an array with these IDs. The active elements are checked in each loop.

this.platform.ready().then(() => {
    let checboxState = this.service.checboxState;
    if (checboxState !== undefined && checboxState.length > 0) {
        checboxState.forEach(checbox => {
            let element = document.getElementById(checbox.id) as HTMLInputElement;
            element.checked = true;
        });
    }
});

But I'm getting this error:

Uncaught (in promise): TypeError: Cannot set property 'checked' of null

Which can be a indicator that Javascript function is firing before DOM is loaded. If that is the case, why platform.ready() isn't working? I tried also with: - document.addEventListener("DOMContentLoaded",() => {} - window.onload = () => {}

but no success.


I managed to make the function work by adding a setTimeout() before the function, so how do I correctly wait the HTML elements to load before firing the JS function?


The checkboxes in the page are loaded by a *ngFor function.


Solution

  • In order to be notified when the checkboxes have been added to the DOM by the *ngFor loop:

    1. Assign a template reference variable to the checkbox elements (e.g. #chk)
    2. Associate the checkbox elements to a QueryList with the help of ViewChildren
    3. Subscribe to the QueryList.changes observable in ngAfterViewInit and process the list of checkboxes in the callback. You can also check directly in ngAfterViewInit to account for cases where the checkbox elements are already rendered
    <input type="checkbox" #chk *ngFor="let id of idList" [id]="id">
    
    @ViewChildren("chk") private checkboxList: QueryList<ElementRef<HTMLInputElement>>;
    
    ...
    
    ngAfterViewInit() {
      // Check if the checkbox is already in the DOM
      this.findCheckBoxWithId("chk36");            
    
      // Find the checkbox if rendered after a delay
      this.checkboxList.changes.subscribe(() => {
        this.findCheckBoxWithId("chk36");          
      });
    }
    
    findCheckBoxWithId(id) {
      const checkbox = this.checkboxList.find(x => x.nativeElement.id === id);
      ...
    }
    

    See this stackblitz for a demo