Home > database >  document.getelementbyid always returns null in angular 13
document.getelementbyid always returns null in angular 13

Time:04-18

I have a component like this

export class NotificationPostComponent extends PostComponent implements OnInit, AfterViewInit {
    commentsDto: IComment[] = [];
    commentId = '';
    ngOnInit(): void {
    this.route.data.subscribe(data=>{
      this.post = data.post;
    });
    this.route.params.subscribe((param : Params) => {
      const slug = param['slug'];      
      this.commentId = param['commentid'];
      this.notifiService.loadCommentsOfPost(slug, this.commentId).subscribe(data=>{
          this.commentsDto = data.comments;       
        })       
    });
    super.ngOnInit();    
  }
  ngAfterViewInit(): void {
    var idDiv = document.getElementById(this.commentId)
    console.log(idDiv)
  }
}

This is a html with id is Guid:

<div id="{{cmt.id}}" *ngFor="let cmt of commentsDto">
  <div>comment</div>
</div>

the problem is that after view is initialized its document.getElementById is always null. Please tell me where did I go wrong?

CodePudding user response:

<!--see the tempalte reference variable "#comment"-->
<div #comment id="{{cmt.id}}" *ngFor="let cmt of commentsDto">
  <div>comment</div>
</div>

//In commentsyou has the "QueryList" (an specie of array)
@ViewChildren('comment') comments:QueryList<ElementRef>

this.notifiService.loadCommentsOfPost(slug, this.commentId).subscribe(data=>{
      this.commentsDto = data.comments;       
      //you neeed wait Angular "render" the elements
      setTimeout(()=>{
        const comment=this.comments
            .find(x=>x.nativeElement.getAttribute('id')==myId)
        if (comment)
            comment.nativeElement.scrollIntoView(
              {Behaivor:"Smooth",Block:Start,inline:"Nearest"})
       })
    })  

Well, you can find using "nativeElement.getAttribute" or find the index of the comment and find the element at this index. some like

        const index=this.commentsDto.findIndex(x=>x.id==myInd)
        const comment=this.comments
            .find((_,i)=>i==index)

CodePudding user response:

Instead of using document.getElementById you can use Angular's built-in API: @ViewChildren.

Then, the inline Template could be extracted into a new component.

Template

<button (click)="scrollToBottom()">Scroll to last comment</button>

<app-comment [title]="comment.title" *ngFor="let comment of comments$ | async">
</app-comment>

<button (click)="scrollToTop()">Scroll to 1st Comment</button>

In the Component, use ViewChildren to access the collection of your CommentComponents represented by a QueryList.

If you need to access the HTML-Element directly, you need to configure @ViewChildren to read the ElementRefence (@ViewChildren(CommentComponent, { read: ElementRef })).

Component

export class NotificationPostComponent extends PostComponent
  implements OnInit, AfterViewInit {
    comments$ = /* Stream of Comments */

    @ViewChildren(CommentComponent, { read: ElementRef })
  commentElementReferences: QueryList<ElementRef<HTMLElement>> | undefined;
}

Next, if you need to scroll to a specific element by a ID or something like that, you can mark each Comment Component with an attr-Binding.

Template

<app-comment
  [title]="comment.title"
  [attr.data-anchor]="comment.id"
  *ngFor="let comment of comments$ | async"
>
</app-comment>

Now, you can add some logic to your component finding the Element you need, allowing you to scroll to it.

Component

private scrollToCommentById(id: string) {
    if (!this.commentElementReferences) return;

    const commentToScrollTo = this.commentElementReferences
      .map((reference) => reference.nativeElement)
      .find((element) => element.getAttribute('data-anchor') === id);

    commentToScrollTo.scrollIntoView({ behavior: 'smooth' });
  }

Finally, you can connect to the activated route, triggering your scrolling mechanism when navigation kicks in.

  ngAfterViewInit(): void {
    // Listen to parameter changes in route
    this.activatedRoute.paramMap
      .pipe(
        map((paramMap) => paramMap.get('id')),
        filter((id) => !!id),
        tap((id) => this.scrollToCommentById(id))
      )
      .subscribe();
  }

Demo

This StackBlitz @ViewChild Demo shows how everything can be set up. There you also see how the button is set up that scrolls to a specific Comment by its ID.

  • Related