J'ai un ensemble de composants de cellules individuelles dans une boucle ng-for . J'ai tout en place mais je n'arrive pas à comprendre le bon
Actuellement j'ai un
setTimeout(() => {
scrollToBottom();
});
Mais cela ne fonctionne pas toujours comme des images de manière asynchrone. Poussez la fenêtre d'affichage vers le bas.
Quel est le moyen approprié de faire défiler jusqu'au bas d'une fenêtre de discussion dans angular2?
J'ai eu le même problème, j'utilise une combinaison AfterViewChecked
et @ViewChild
(Angular2 beta.3).
Le composant:
import {..., AfterViewChecked, ElementRef, ViewChild, OnInit} from 'angular2/core'
@Component({
...
})
export class ChannelComponent implements OnInit, AfterViewChecked {
@ViewChild('scrollMe') private myScrollContainer: ElementRef;
ngOnInit() {
this.scrollToBottom();
}
ngAfterViewChecked() {
this.scrollToBottom();
}
scrollToBottom(): void {
try {
this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
} catch(err) { }
}
}
Le gabarit:
<div #scrollMe style="overflow: scroll; height: xyz;">
<div class="..."
*ngFor="..."
...>
</div>
</div>
Bien sûr, c'est assez basique. La AfterViewChecked
se déclenche chaque fois que la vue est vérifiée:
Implémentez cette interface pour être averti après chaque vérification de la vue de votre composant.
Si vous avez un champ de saisie pour envoyer des messages, par exemple, cet événement est déclenché après chaque augmentation de touche (juste pour donner un exemple). Mais si vous enregistrez si l'utilisateur a défilé manuellement puis ignoré la scrollToBottom()
, tout devrait bien se passer.
La plus simple et la meilleure solution pour cela est:
Ajoutez ce #scrollMe [scrollTop]="scrollMe.scrollHeight"
chose simple sur le côté Modèle
<div style="overflow: scroll; height: xyz;" #scrollMe [scrollTop]="scrollMe.scrollHeight">
<div class="..."
*ngFor="..."
...>
</div>
</div>
Voici le lien pour WORKING DEMO (avec l'application de chat factice) ET CODE COMPLET
Est-ce que travailler avec Angular2 et aussi jusqu'à 5, comme ci-dessus démo est fait dans Angulaire5.
Remarque :
Pour erreur:
ExpressionChangedAfterItHasBeenCheckedError
S'il vous plaît vérifier votre css, c'est une question de côté css, pas le côté angulaire , L’un des utilisateurs @KHAN a résolu ce problème en supprimant
overflow:auto; height: 100%;
dediv
. (veuillez vérifier les conversations pour plus de détails)
Pensez à utiliser
.scrollIntoView()
Voir https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
J'ai ajouté une vérification pour voir si l'utilisateur a essayé de faire défiler vers le haut.
Je vais juste laisser ça ici si quelqu'un le veut :)
<div class="jumbotron">
<div class="messages-box" #scrollMe (scroll)="onScroll()">
<app-message [message]="message" [userId]="profile.userId" *ngFor="let message of messages.slice().reverse()"></app-message>
</div>
<textarea [(ngModel)]="newMessage" (keyup.enter)="submitMessage()"></textarea>
</div>
et le code:
import { AfterViewChecked, ElementRef, ViewChild, Component, OnInit } from '@angular/core';
import {AuthService} from "../auth.service";
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/concatAll';
import {Observable} from 'rxjs/Rx';
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-messages',
templateUrl: './messages.component.html',
styleUrls: ['./messages.component.scss']
})
export class MessagesComponent implements OnInit {
@ViewChild('scrollMe') private myScrollContainer: ElementRef;
messages:Array<MessageModel>
newMessage = ''
id = ''
conversations: Array<ConversationModel>
profile: ViewMyProfileModel
disableScrollDown = false
constructor(private authService:AuthService,
private route:ActivatedRoute,
private router:Router,
private conversationsApi:ConversationsApi) {
}
ngOnInit() {
}
public submitMessage() {
}
ngAfterViewChecked() {
this.scrollToBottom();
}
private onScroll() {
let element = this.myScrollContainer.nativeElement
let atBottom = element.scrollHeight - element.scrollTop === element.clientHeight
if (this.disableScrollDown && atBottom) {
this.disableScrollDown = false
} else {
this.disableScrollDown = true
}
}
private scrollToBottom(): void {
if (this.disableScrollDown) {
return
}
try {
this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
} catch(err) { }
}
}
Aucune de ces réponses n'est même à moitié décente. La réponse acceptée est déclenchée lors du défilement des messages.
Vous voulez un modèle comme celui-ci.
<div #content>
<div #messages *ngFor="let message of messages">
{{message}}
</div>
</div>
Ensuite, vous souhaitez utiliser une annotation ViewChildren pour vous abonner aux nouveaux éléments de message ajoutés à la page.
@ViewChildren('messages') messages: QueryList<any>;
@ViewChild('content') content: ElementRef;
ngAfterViewInit() {
this.messages.changes.subscribe(this.scrollToBottom);
}
scrollToBottom = () => {
try {
this.content.nativeElement.scrollTop = this.content.nativeElement.scrollHeight;
} catch (err) {}
}
Si vous voulez être sûr de faire défiler l'écran jusqu'au bout après que * ngFor soit terminé, vous pouvez l'utiliser.
<div #myList>
<div *ngFor="let item of items; let last = last">
{{item.title}}
{{last ? scrollToBottom() : ''}}
</div>
</div>
scrollToBottom() {
this.myList.nativeElement.scrollTop = this.myList.nativeElement.scrollHeight;
}
Important ici, la variable "last" définit si vous êtes actuellement au dernier élément, vous pouvez donc déclencher la méthode "scrollToBottom"
this.contentList.nativeElement.scrollTo({left: 0 , top: this.contentList.nativeElement.scrollHeight, behavior: 'smooth'});
Dans l’utilisation angulaire de matériaux sidenav, je devais utiliser les éléments suivants:
let ele = document.getElementsByClassName('md-sidenav-content');
let eleArray = <Element[]>Array.prototype.slice.call(ele);
eleArray.map( val => {
val.scrollTop = val.scrollHeight;
});
const element = document.getElementById('box');
element.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' });
La réponse de Vivek a fonctionné pour moi, mais a abouti à une expression a changé après avoir été vérifié erreur. Aucun des commentaires n'a fonctionné pour moi, mais j'ai changé la stratégie de détection des changements.
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'page1',
templateUrl: 'page1.html',
})
Partageant ma solution, car je n'étais pas complètement satisfait du reste. Mon problème avec AfterViewChecked
est que parfois je défile vers le haut, et pour une raison quelconque, ce crochet de vie s'appelle et il me fait défiler même s'il n'y a pas de nouveaux messages. J'ai essayé d'utiliser OnChanges
mais ceci était un problème qui m'a amené à ceci solution. Malheureusement, n'utilisant que DoCheck
, il faisait défiler l'écran avant le rendu des messages, ce qui n'était d'aucune utilité. Je les ai donc combinés pour que DoCheck indique fondamentalement AfterViewChecked
s'il doit appeler scrollToBottom
.
Heureux de recevoir des commentaires.
export class ChatComponent implements DoCheck, AfterViewChecked {
@Input() public messages: Message[] = [];
@ViewChild('scrollable') private scrollable: ElementRef;
private shouldScrollDown: boolean;
private iterableDiffer;
constructor(private iterableDiffers: IterableDiffers) {
this.iterableDiffer = this.iterableDiffers.find([]).create(null);
}
ngDoCheck(): void {
if (this.iterableDiffer.diff(this.messages)) {
this.shouldScrollDown = true;
}
}
ngAfterViewChecked(): void {
if (this.shouldScrollDown) {
this.scrollToBottom();
this.shouldScrollDown = false;
}
}
scrollToBottom() {
try {
this.scrollable.nativeElement.scrollTop = this.scrollable.nativeElement.scrollHeight;
} catch (e) {
console.error(e);
}
}
}
chat.component.html
<div class="chat-wrapper">
<div class="chat-messages-holder" #scrollable>
<app-chat-message *ngFor="let message of messages" [message]="message">
</app-chat-message>
</div>
<div class="chat-input-holder">
<app-chat-input (send)="onSend($event)"></app-chat-input>
</div>
</div>
chat.component.sass
.chat-wrapper
display: flex
justify-content: center
align-items: center
flex-direction: column
height: 100%
.chat-messages-holder
overflow-y: scroll !important
overflow-x: hidden
width: 100%
height: 100%