ionic 2 + angular 2: автоматическая прокрутка вниз списка / страницы / чата
Я пытаюсь закодировать страницу с двумя сегментами "чат"и " контент". Я хочу, чтобы один сегмент" чата " автоматически прокручивал страницу вниз без эффекта. Чат-это <ion-list>
несколько <ion-item>
.
<ion-list>
<ion-item> item 1 </ion-item>
<ion-item> item 2 </ion-item>
....
<ion-item> item 20 </ion-item>
<ion-item> item 21 </ion-item> <!-- user should see directly this item on bottom of the page -->
</ion-list>
Я использую Javascript, а не typescript, и я не хочу использовать jQuery. Спасибо :) Кроме того, когда я иду в сегмент "контент" и возвращаюсь в "чат", я хочу снова прокрутить чат.
8 ответов
вот как я это сделал:
chatPage.HTML-код
<ion-content #content padding class="chatPage">
<ion-list no-lines>
<ion-item *ngFor="let msg of messages" >
<chat-bubble [message]="msg"></chat-bubble>
</ion-item>
</ion-list>
</ion-content>
важный момент в chatPage.HTML-код is #content
on <ion-content>
. Я использую #content
идентификатор для получения ссылки на <ion-content>
в своем chatPage.js С помощью ViewChild
.
теперь для фактической логики прокрутки:
chatPage.js
import {Component, ViewChild} from '@angular/core';
@Component({
templateUrl: 'build/pages/chatPage/chatPage.html',
queries: {
content: new ViewChild('content')
}
})
export class ChatPage {
constructor() {
}
//scrolls to bottom whenever the page has loaded
ionViewDidEnter(){
this.content.scrollToBottom(300);//300ms animation speed
}
}
кроме того, всякий раз, когда мой chatPage нужно показать еще одно сообщение чата в списке (либо получено новое сообщение, либо отправлено новое сообщение), я использую приведенный ниже код для прокрутки до нового дна:
setTimeout(() => {
this.content.scrollToBottom(300);//300ms animation speed
});
обновление для Typescript
когда я дал этот ответ, я работал с JavaScript-версией проекта Ionic 2. Со временем я переключился на TypeScript, но забыл обновить ответ, поэтому вот небольшое обновление для chatPage.js (ts):
chatPage.ТС
import {Component, ViewChild} from '@angular/core';
@Component({
templateUrl: 'chatPage.html'
})
export class ChatPage {
@ViewChild('content') content:any;
constructor() { }
//scrolls to bottom whenever the page has loaded
ionViewDidEnter(){
this.content.scrollToBottom(300);//300ms animation speed
}
}
Ionic имеет возможность сделать это и работает довольно хорошо: и это наиболее подходящий способ с angular 2+ и Ionic.
import { Content } from ‘ionic-angular’; export class CommentsPage{ @ViewChild(Content) content: Content; ionViewWillEnter(): void { this.scrollToBottom(); } scrollToBottom() { setTimeout(() => { this.content.scrollToBottom(); }); } }
во-первых, @rinukkusu ответ правильный, но он не работает в моем случае, потому что <ion-content>
(родитель <ion-list>
) имеет некоторые ошибки с ним (над этим работают ионные разработчики), поэтому мне пришлось поместить этот элемент с scroll:hidden
и создайте второй контент внутри, чтобы применить автоматическую прокрутку.
Наконец, с помощью правого (ых)css я вызвал функцию on construtor
когда страница загружается, а затем каждый раз, когда пользователи нажимают на сегмент "чат".
чат.HTML-код
<!-- I create the segment and call the `autoScroll()` when users clicks on "chat" -->
<ion-toolbar primary class="toolbar-segment">
<ion-segment light [(ngModel)]="segment">
<ion-segment-button value="chat" (click)="autoScroll()">
Chat
</ion-segment-button>
<ion-segment-button value="profile">
Profile
</ion-segment-button>
</ion-segment>
</ion-toolbar>
<!--I wrote the css inline just as example.
DON'T do it on your project D: -->
<!-- overflow:hidden prevent the ion-content to scroll -->
<ion-content [ngSwitch]="segment" style="overflow: hidden;">
<!-- height: 100% to force scroll if the content overflows the container.
#chat-autoscroll is used by javascript.-->
<div class="content-scroll" id="chat-autoscroll" *ngSwitchWhen="'chat'" style="height: 100%; overflow: scroll">
(... make sure the content is bigger
than the container to see the auto scroll effect ...)
</div>
<!-- here it's just a normal scroll -->
<div *ngSwitchWhen="'profile'" class="content-scroll" style="height: 100%; overflow: auto">
(... content of profile segment ...)
</div>
</ion-content>
чат.js
constructor () {
// when the user opens the page, it shows the "chat" segment as initial value
this.segment = 'chat';
// when page loads, it calls autoScroll();
if (this.segment == 'chat') {
console.log('chat');
this.autoScroll();
};
}
/*Here comes the tricky.
If you don't use setTimeout, the console will say that
#chat-autoscroll doesn't exist in the first call (inside constructor).
This happens because the script runs before the DOM is ready.
I did a workaround with a timeOut of 10ms.
It's enough time to DOM loads and then autoScroll() works fine.
*/
autoScroll() {
setTimeout(function () {
var itemList = document.getElementById("chat-autoscroll");
itemList.scrollTop = itemList.scrollHeight;
}, 10);
}
вывод: Функция вызывается дважды. Когда страница загружается (конструктор) и каждый раз пользователь возвращается в сегмент "чат". (click)= " autoScroll ()"
надеюсь, это кому-то поможет. Если вы знаете лучший способ, дайте мне знать! Я начал играть с Angular2 и Ionic2 пару недель назад, поэтому здесь много концепций/баз, которые я мог бы пропустить.
спасибо :)
другое решение - "наблюдать" за изменениями в виде прокрутки и прокручивать автоматически. т. е. если есть новые элементы HTML, где появляются сообщения, прокрутите вниз.
export class MessagesPage implements OnInit, OnDestroy {
autoScroller: MutationObserver;
ngOnInit() {
this.autoScroller = this.autoScroll();
}
ngOnDestroy() {
this.autoScroller.disconnect();
}
autoScroll(): MutationObserver {
const autoScroller = new MutationObserver(this.scrollDown.bind(this));
autoScroller.observe(this.messagesList, {
childList: true,
subtree: true
});
return autoScroller;
}
scrollDown(): void {
this.scroller.scrollTop = this.scroller.scrollHeight;
this.messageEditor.focus();
}
private get messageEditor(): HTMLInputElement {
return <HTMLInputElement>document.querySelector('ion-input');
}
private get messagesList(): Element {
return document.querySelector('.messages');
}
private get scroller(): Element {
return this.messagesList.querySelector('.scroll-content');
}
}
шаблон:
<ion-content>
<ion-scroll scrollY="true" class="messages">
<ion-list>
<div *ngFor="let message of messages">
<p [innerHtml]="message.text"></p>
</div>
</ion-list>
</ion-scroll>
</ion-content>
<ion-footer>
<ion-toolbar>
<ion-input [(ngModel)]="message" type="text" placeholder="Write a message..." autocomplete="true" spellcheck="true" tappable></ion-input>
<ion-buttons end>
<button ion-button icon-only (click)="sendMessage()">
<ion-icon name="paper-plane"></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
</ion-footer>
(взять из Ionic2CLI-Метеор-WhatsApp на Github)
Это может помочь кому-то, я дал аналогичный ответ в ионный форум. Это реализовано в Ionic 3.
Я ngAfterViewChecked()
для достижения этой функции. Ниже приведена моя структура кода -
дома.в HTML -
<ion-content padding>
<div #scrollMe id="messages-container" style="overflow: auto; height: 100%;">
// All messages elemets. Scroll
</div>
</ion-content>
дома.ТС -
import { Component,ViewChild, AfterViewChecked, * * } from '@angular/core';
.
.
export class HomePage implements AfterViewChecked{
// used for scrolling the pane
@ViewChild('scrollMe') private myScrollContainer: ElementRef;
ngAfterViewChecked() {
this.scrollToBottom();
}
scrollToBottom(): void {
// method used to enable scrolling
this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
}
}
.scrollTop
и .scrollHeight
свойства в JavaScript, посмотреть здесь.
Я добавил проверку, чтобы увидеть, пытался ли пользователь прокрутить вверх.
<div style="overflow: scroll; height: xyz;" #scrollMe [scrollTop]="scrollMe.scrollHeight">
<div class="..."
*ngFor="..."
...>
</div>
</div>
Это должно работать, если ваш ion-list
элемент имеет фиксированную высоту.
<ion-list id="item-list">
<ion-item> item 1 </ion-item>
<ion-item> item 2 </ion-item>
[...]
<ion-item> item 20 </ion-item>
<ion-item> item 21 </ion-item>
</ion-list>
[...]
<script>
var itemList = document.getElementById("item-list");
itemList.scrollTop = itemList.scrollHeight
</script>