web-dev-qa-db-fra.com

ionic 2 + angular 2: défilement automatique vers le bas de la liste / page / chat)

J'essaie de coder une page avec deux segments "chat" et "contenu". Je veux qu'un segment "chat" fasse défiler automatiquement la page vers le bas sans effet. Le chat est un <ion-list> avec plusieurs <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>

J'utilise Javascript, pas TypeScript, et je ne veux pas utiliser jQuery. Merci :) De plus, lorsque je vais au segment "contenu" et que je reviens à "chat", je veux faire défiler à nouveau automatiquement le chat.

12
sandrina-p

Tout d'abord, la réponse @rinukkusu est correcte, mais cela ne fonctionne pas dans mon cas, car <ion-content> (parent de <ion-list>) a quelques bugs (les développeurs ioniques y travaillent), j'ai donc dû mettre cet élément avec scroll:hidden et créez un deuxième contenu à l'intérieur pour appliquer le défilement automatique. Enfin avec le (s) bon (s) css j'ai appelé la fonction sur construtor lors du chargement de la page puis à chaque fois que les utilisateurs cliquent sur le segment "chat".

chat.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>

chat.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);
}

Conclusion: La fonction est appelée deux fois. Lorsque la page est chargée (constructeur) et à chaque fois que l'utilisateur revient au segment "chat". (click) = "autoScroll ()"

J'espère que ça aidera quelqu'un. Si vous connaissez mieux, faites le moi savoir! J'ai commencé à jouer avec Angular2 et Ionic2 il y a quelques semaines donc il y a beaucoup de concepts/bases qui pourraient me manquer ici.

Merci :)

5
sandrina-p

Voici comment je l'ai fait:

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>

Le bit important dans chatPage.html est #content sur <ion-content>. Je vais utiliser le #content identifiant pour obtenir une référence à <ion-content> dans mon chatPage.js en utilisant le ViewChild.

Maintenant, pour la logique de défilement réelle:

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
  }
}

De plus, chaque fois que mon chatPage doit afficher un autre message de chat dans la liste (soit un nouveau message est reçu, soit un nouveau message est envoyé), j'utilise le code ci-dessous pour faire défiler vers le bas :

setTimeout(() => {
  this.content.scrollToBottom(300);//300ms animation speed
});

mise à jour pour TypeScript

À l'époque où j'ai donné cette réponse, je travaillais avec la version JavaScript du projet Ionic 2. Au fil du temps, je suis passé à TypeScript mais j'ai oublié de mettre à jour la réponse, alors, voici une petite mise à jour pour chatPage.js (ts):

chatPage.ts

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
  }
}
28
smukov

Le Ionic a une option pour faire cela et fonctionne plutôt bien: Et c'est la manière la plus appropriée avec angular 2+ et Ionic).

 importez {Content} de 'ionic-angular'; 
 
 exportez la classe CommentsPage {
 @ ViewChild (Content) content: Content; 
 
 ionViewWillEnter (): void {
 this.scrollToBottom (); 
} 
 
 scrollToBottom () {
 setTimeout (() => {
 this.content.scrollToBottom (); 
}); 
} 
} 
18
itsme_seenu

Cela pourrait aider quelqu'un, j'ai donné une réponse similaire in Ionic forum . Ceci est implémenté dans Ionic 3.

J'ai utilisé ngAfterViewChecked() pour réaliser cette fonctionnalité. Voici ma structure de code -

home.html -

<ion-content padding>
    <div #scrollMe id="messages-container" style="overflow: auto; height: 100%;">
        // All messages elemets. Scroll
    </div>
</ion-content>

home.ts -

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 et .scrollHeight sont des propriétés JavaScript, voir ici .

1
Sujit Kumar Singh

Une autre solution consiste à "observer" les changements dans la vue de défilement et à faire défiler automatiquement. c'est-à-dire s'il y a de nouveaux éléments HTML où les messages apparaissent, faites défiler vers le bas.

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');
  }
}

Modèle:

<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>

(prendre de Ionic2CLI-Meteor-WhatsApp sur Github )

1
Gianfranco P.

J'ai ajouté une vérification pour voir si l'utilisateur a essayé de faire défiler vers le haut.

<div style="overflow: scroll; height: xyz;" #scrollMe [scrollTop]="scrollMe.scrollHeight">
    <div class="..." 
        *ngFor="..."
        ...>  
    </div>
</div>
1
Saravanan Nandhan