Search code examples
angular

Wait for datasource to load in Angular


I have some dynamic content being loaded in the html, using material tables, and linking the datasource to a signal, like this:

    <table
      mat-table
      [dataSource]="[resultEntries$()][0]"
      (contentChanged)="scrollToFourth()"
    >
      <ng-container matColumnDef="col-lemma">
        <td mat-cell *matCellDef="let element">
          <a
            [id]="element.alpha"
            class="link-primary"
            role="button"
            (click)="clickID(element)"
            >{{ element.l }}</a
          >
        </td>
      </ng-container>
      <ng-container matColumnDef="col-brp">
        <td mat-cell *matCellDef="let element">{{ element.bp }}</td>
      </ng-container>
      <ng-container matColumnDef="col-amp">
        <td mat-cell *matCellDef="let element">{{ element.ap }}</td>
      </ng-container>
      <tr
        mat-row
        class="material-row"
        *matRowDef="let row; columns: displayedColumns"
      ></tr>
    </table>

And I need to run this function, in which I use the oldfashioned getElementById to access the DOM element (I've tried ViewChild to no avail).

  scrollToFourth(): void {
    let delayInMilliseconds = 50;
    while (!this.resultEntry$()[0].alpha) {
      setTimeout(() => {}, delayInMilliseconds);
    }
    document
      .getElementById(this.resultEntry$()[0].alpha.toString())!
      .scrollIntoView({
        behavior: 'instant',
      });
  }

The problem is that it runs too early, and it gives me an error saying that the DOM element doesn't exist. If I move everything into ngAfterViewInit, it's still too early. The only way it works is to put it inside a setTimeout with a four-second delay, but of course, the exact number of seconds will depend on the client and server, so this is hardly a good alternative. Any help will be greatly appreciated.


Solution

  • If your're using signal as data source, why not use "effects". It's true that you need enclosed the action in a setTimeout -without delay time- to say to Angular: "hey, change the data source and, when you finished of draw remember that you need makes this another sentences"

      readonly dataSource = signal(ELEMENT_DATA);
      @ViewChild('id') el!:ElementRef
      constructor(){
        effect(()=>{
          const dataSource=this.dataSource();
          //the setTimeout it's only to makes the actions after "repaint"
          //but see that have no "delay time"
          setTimeout(()=>{
            this.el.nativeElement.scrollIntoView({
              behavior: 'instant',
            });
          })
        })
      }
    

    stackblitz