Search code examples
angularlistviewnativescript

NativeScript: Scrollableheight is always 0


I've made a Chat and the function that i'm trying to implement is that the chat should always scroll to the last message.

html:

<ActionBar>
    <NavigationButton (tap)="onBackTap()" android.systemIcon="ic_menu_back"></NavigationButton>
    <Label [text]="user.name"></Label>
</ActionBar>
<ScrollView #scrollLayout style="height: 80%;margin-top: -200px">
      <ListView class="chat-body" [items]="messages" separatorColor="transparent" style="min-height:100%" >
            <ng-template let-message="item" let-odd="odd" let-even="even">
                <GridLayout rows ="auto, *">
                    <Label style="padding-right:150px" *ngIf="message.sender == me" horizontalAlignment="right" class="chat-message-me" [text]="message.msg" textWrap="true"></Label>
                    <Label *ngIf="message.sender == me" horizontalAlignment="right" class="chat-timestamp" verticalAlignment="bottom" [text]="message.time"></Label>
                
                    <Label  style="padding-left:170px" *ngIf="message.sender != me" horizontalAlignment="left" class="chat-message-you" [text]="message.msg" textWrap="true"></Label>
                    <Label *ngIf="message.sender != me" horizontalAlignment="left" verticalAlignment="bottom" class="chat-timestamp" [text]="message.time"></Label>
                </GridLayout>
            </ng-template>
     </ListView>
</ScrollView>
<StackLayout  class="nt-form chat-wrapper"  >
    <StackLayout loaded="test" id="chat-form" orientation="horizontal" class="nt-input input-field form">
         <TextField  
           (textChange)="onTextChange($event)" 
           [text]="inputText"
           width="800px"  
           class="-rounded-lg input" 
           hint="Nachricht" 
           style="background-color: #ffffff;"
         ></TextField>
        <button width="120px" class="-rounded-lg fa far fas fab" (tap)="sendMessage();" style="margin-left: 15px;" text="&#xf1d8;"></button>
    </StackLayout>
</StackLayout>

ts:

@Component({
    selector: "ItemDetail",
    styleUrls: ['item-detail.component.css'],
    templateUrl: "./item-detail.component.html",
    providers: [DatePipe]
})
export class ItemDetailComponent implements OnInit {

 @ViewChild('scrollLayout') scrollen: ElementRef;

constructor(){}

ngOnInit(): void {
 }

    ngAfterViewInit(): void{     
        setTimeout(()=> {
            var scroller = this.scrollen.nativeElement.scrollableHeight
            console.log("height", scroller)
            this.scrollen.nativeElement.scrollToVerticalOffset(this.scrollen.nativeElement.scrollableHeight, false);
        }, 1000);     
    }

It looks like this:

enter image description here

But should be like this(scrolled down):

enter image description here

The console log in NgAfterViewInit is always "height 0" so it tells me that ListView is 0? How can i get it to scroll down programmaticly?


Solution

  • I think the problem is with the nesting of a ListView inside a ScrollView. ListView already comes with scrolling behavior by default, so you shouldn't need to nest it within a ScrollView.

    <ListView class="chat-body" [items]="messages" separatorColor="transparent" style="min-height:100%">
        <ng-template let-message="item" let-odd="odd" let-even="even">
          <GridLayout rows ="auto, *">
            <Label style="padding-right:150px" *ngIf="message.sender == me" horizontalAlignment="right" class="chat-message-me" [text]="message.msg" textWrap="true"></Label>
            <Label *ngIf="message.sender == me" horizontalAlignment="right" class="chat-timestamp" verticalAlignment="bottom" [text]="message.time"></Label>        
            <Label  style="padding-left:170px" *ngIf="message.sender != me" horizontalAlignment="left" class="chat-message-you" [text]="message.msg" textWrap="true"></Label>
            <Label *ngIf="message.sender != me" horizontalAlignment="left" verticalAlignment="bottom" class="chat-timestamp" [text]="message.time"></Label>
        </GridLayout>
      </ng-template>
    </ListView>
    

    Scrolling to bottom

    Instead of ngAfterViewInit, we can use the ListViews loaded event to make sure that the listview is loaded and ready to be scrolled.

    <ListView (loaded)="listViewLoaded($event)"
    class="chat-body" [items]="messages" separatorColor="transparent" style="min-height:100%">
     ...
    </ListView>
    
    listViewLoaded(args: EventData) {
      const listView = args.object as ListView;
    
      const lastIndex = this.messages.length - 1;
    
      // you might need a setTimeout here if it still fires too early
      listView.scrollToIndex(lastIndex)
    
      // or this if you want the scroll to be animated
      listView.scrollToIndexAnimated(lastIndex)
    }