2

I have a div with messages and I want to make sure when all the chat information has been retrieve to scroll the div down to the bottom to the last message, but it doesn't work it gives me this error:

Cannot read property 'scrollHeight' of null

TS CHAT COMPONENT

 ngOnInit() {
    this.chat$ = this._cs.joinUsers(source).pipe(tap(() => this.scrollBottom()));
  }

  scrollBottom() {
    const CHAT_BODY = document.getElementById("card-body");
    CHAT_BODY.scrollTop = CHAT_BODY.scrollHeight;
  }

Notes: The card-body element is being loaded by the chat$ | async

I have tried to use finallize(), and ngAfterViewInit() and no luck. BUT if I copy past my code from the scrollBottom() in the console after I see everything on the page, it works fine. So the problem is that is getting trigger and the Observable it's not done yet. maybe?

My question is different than other solutions I have seen on SO because they aren't using observable an the element don't exist yet when the observable is being triggered.

UPDATE:

I Tried accesing the element using ViewChild and got this error:

I got this error: property 'nativeElement' of undefined

  @ViewChild("cardBody")
  cardBody: ElementRef;
  ngAfterViewInit() {
    console.log("Values on ngAfterViewInit():");
    console.log(this.cardBody.nativeElement );
  }

HTML

 <ng-container *ngIf="(chat$ | async) as chat">
    <ng-container *ngIf="(auth.user$ | async) as user">
      .
      .
      <div class="card-body msg_card_body" id="card-body" #cardBody>
      .
      .
      .
      </div>
  </ng-container>
</ng-container>
Patricio Vargas
  • 5,236
  • 11
  • 49
  • 100
  • Why are you executing your `scrollBottom` on `tap`? Why don't you subscribe to your observable? It could be the case, try to subscribe to it and maybe it will fix your issue – benshabatnoam Jan 13 '19 at 19:30
  • @ggradnig it is getting trigger. If i put console.log it prints it fine. the problem is the dom element hasn't been loaded – Patricio Vargas Jan 13 '19 at 19:33
  • I see, use `ngAfterViewInit` instead of `ngOnInit` ;-) – ggradnig Jan 13 '19 at 19:35
  • @ggradnig I tried that. I mentioned that in the question :( thanks tho! – Patricio Vargas Jan 13 '19 at 19:36
  • 1
    ah sorry, didn't read that, my fault. have you tried to get hold of the component by using `@ViewChild` ? That should give you a "safe" reference. https://angular.io/api/core/ViewChild – ggradnig Jan 13 '19 at 19:38
  • @benshabatnoam didn't work ` this.chat$.subscribe(() => this.scrollBottom());` – Patricio Vargas Jan 13 '19 at 19:39
  • @ggradnig ohh. let me try that! – Patricio Vargas Jan 13 '19 at 19:39
  • @ggradnig tried to do that by accessing the viewchild in the afterOnInit. I got this error: property 'nativeElement' of undefined – Patricio Vargas Jan 13 '19 at 19:45
  • 1
    I see, that means that the view child wasn't selected correctly. could you add the template where 'card-body' is set to the question? – ggradnig Jan 13 '19 at 19:47
  • @ggradnig I have updated the question – Patricio Vargas Jan 13 '19 at 19:48
  • 1
    Please give more context in the HTML markup, especially if the `cardBody` is inside an `ngFor` or an `ngIf` directive. – ConnorsFan Jan 13 '19 at 19:51
  • @ConnorsFan I have updated my question. please take a look to the HTML part – Patricio Vargas Jan 13 '19 at 19:53
  • 1
    Possible duplicate of [Angular: unable to scroll down to bottom in element](https://stackoverflow.com/questions/53123912/angular-unable-to-scroll-down-to-bottom-in-element) – ConnorsFan Jan 13 '19 at 19:54
  • Try accessing the `cardBody` elements in the `QueryList.changes` event handler, as shown in [this duplicate question](https://stackoverflow.com/a/53124292/1009922). – ConnorsFan Jan 13 '19 at 19:56
  • 1
    Possible duplicate of [angular2 scroll to bottom (chat style)](https://stackoverflow.com/questions/35232731/angular2-scroll-to-bottom-chat-style) – jcal Jan 13 '19 at 19:56
  • @ConnorsFan those solutions didn't work since they problem here is the Observable. Thanks tho! I got it working with a setTimeout :( – Patricio Vargas Jan 13 '19 at 20:03
  • You shouldn't have to use `setTimeout`. With `ViewChildren` and `QueryList.changes`, you get notified when the elements have been added to the DOM. – ConnorsFan Jan 13 '19 at 20:08
  • I used the setTimeout without the viewChild. And the query changes won't work because the element doesn't exist when the ngafterview init is trigger due to the observable :( – Patricio Vargas Jan 13 '19 at 20:10
  • 1
    The elements don't exist in `ngAfterViewInit`, but they exist in the `QueryList.changes` callback. Using `setTimeout` is not the correct way to handle this kind of situation. – ConnorsFan Jan 13 '19 at 20:11
  • @jcal got it working. Thanks so much for your help! – Patricio Vargas Jan 13 '19 at 20:14
  • @ConnorsFan thanks so much for your help. I will do more research on QueryList. I got it working – Patricio Vargas Jan 13 '19 at 20:14

1 Answers1

1

Finally was able to get the solutions to this:

Solution 1)

 <div  class="card-body msg_card_body" id="card-body" #cardBody
  [scrollTop]="cardBody.scrollHeight">
 .
 .
 </div>

Solution 2)

ngAfterViewInit() {
    setTimeout(() => {
     this.scrollBottom();
    }, 500);  
}
scrollBottom() {
  const CHAT_BODY = document.getElementById("card-body");
  CHAT_BODY.scrollTop = CHAT_BODY.scrollHeight;
}
Patricio Vargas
  • 5,236
  • 11
  • 49
  • 100