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>

Просто сделай это:

public ionViewDidEnter(){
    this.content.scrollToBottom(300);
}