tilly
tilly

Reputation: 2620

Angular: unable to scroll down to bottom in element

I've been postponing fixing this error that I have been having for a while now. I have the below chatwindow:

enter image description here

The window where I display the messages is a separate component (chat-window.component.ts). I want to scroll to the bottom with ngOnChanges.

When we receive the conversation with the messages from the parent component, where it is received from the server via an asynchronous request, we want to scroll to the bottom of the window element. We do this by calling the this.scrollToBottom() method of the class in the ngOnChanges lifecycle hook.

This.scrollToBottom does get called, but it doesn't scroll to the bottom of the element. Can someone see why?

chat-window.component.ts: in ngOnchanges we do some synchronous stuff before we call this.scrollToBottom()

export class ChatboxWindowComponent implements OnChanges, OnInit, AfterViewChecked {

  @Input('conversation') conversation;
  @ViewChild('window') window;

  constructor() { }

  ngOnChanges() {
    // If the date separators have already been added once, we avoid doing it a second time
    const existingDateObj = this.conversation.messages.findIndex((item, i) => item.dateObj);

    if (existingDateObj === -1) {
      this.conversation.messages.forEach( (item, index, array) => {
        if (index !== 0) {
          const date1 = new Date(array[index - 1].date);
          const date2 = new Date(item.date);

          if (date2.getDate() !== date1.getDate() || date2.getMonth() !== date1.getMonth()) {
            this.conversation.messages.splice(index, 0, {date: date2, dateObj: true});
            console.log(this.conversation.messages.length);
          }
        }
      });
    }

    this.scrollToBottom();
  }

  ngOnInit() {
  }

  ngAfterViewChecked() {
  }

  isItMyMsg(msg) {
    return msg.from._id === this.conversation.otherUser.userId;
  }

  scrollToBottom() {
    try {
      console.log('scrollToBottom called');
      this.window.nativeElement.top = this.window.nativeElement.scrollHeight;
    } catch (err) {}
  }
}

chat-window.component.html

<div #window class="window">
  <ng-container *ngFor="let message of conversation.messages">
    <div class="date-container" *ngIf="!message.msg; else windowMsg">
      <p class="date">{{message.date | amDateFormat:'LL'}}</p>
    </div>
    <ng-template #windowMsg>
      <p
        class="window__message"
        [ngClass]="{
    'window__message--left': isItMyMsg(message),
    'window__message--right': !isItMyMsg(message)
    }"
      >
        {{message.msg}}
      </p>
    </ng-template>
  </ng-container>
</div>

Upvotes: 3

Views: 15425

Answers (4)

Vidura Karunarathna
Vidura Karunarathna

Reputation: 421

  • Create a function that handles the scrolling

    goToBottom(){ window.scrollTo(0,document.body.scrollHeight); }

  • Add it in ngAfterViewChecked lifycycle

    ngAfterViewChecked() { this.goToBottom(); }

Upvotes: 0

Mukul Raghav
Mukul Raghav

Reputation: 489

You can use this code

<div id="focusBtn"></div>

const element = document.getElementById("focusBtn");
element.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });

Upvotes: 0

Tharindu Lakshan
Tharindu Lakshan

Reputation: 6206

You can add the following code into your HTML element.

#window [scrollTop]="window.scrollHeight" *ngIf="messages.length > 0"

Full code according to your code sample as follows,

<div #window [scrollTop]="window.scrollHeight" *ngIf="messages.length > 0" class="window">
  <ng-container *ngFor="let message of conversation.messages">
    <div class="date-container" *ngIf="!message.msg; else windowMsg">
      <p class="date">{{message.date | amDateFormat:'LL'}}</p>
    </div>
    <ng-template #windowMsg>
      <p
        class="window__message"
        [ngClass]="{
    'window__message--left': isItMyMsg(message),
    'window__message--right': !isItMyMsg(message)
    }"
      >
        {{message.msg}}
      </p>
    </ng-template>
  </ng-container>
</div>

This is work for me. (Currently, I'm using Angular 11) 😊👍

Upvotes: 3

Martin Parenteau
Martin Parenteau

Reputation: 73761

The scroll doesn't work because the list of messages is not rendered yet when you call scrollToBottom. In order to scroll once the messages have been displayed, set a template reference variable (e.g. #messageContainer) on the message containers:

<ng-container #messageContainer *ngFor="let message of conversation.messages">
  ...
</ng-container>

In the code, you can then access these elements with ViewChildren and scroll the window when the QueryList.changes event is triggered:

@ViewChildren("messageContainer") messageContainers: QueryList<ElementRef>;

ngAfterViewInit() {
  this.scrollToBottom(); // For messsages already present
  this.messageContainers.changes.subscribe((list: QueryList<ElementRef>) => {
    this.scrollToBottom(); // For messages added later
  });
}

Upvotes: 9

Related Questions