4

In my Nativescript Angular application, I have a TextView within a ScrollView, defined as such:

<ScrollView orientation="vertical" height="70%" width="100%" style="margin-bottom: 1%; background-color:white" (loaded)="onScrollerLoaded($event)">
    <TextView 
        id="terminal" [text]="terminalText" editable="false"
        style="color: whitesmoke; background-color: black; font-size: 8%; font-family: monospace" height="100%"     
        (tap)="onTap($event)" (loaded)="onTerminalLoaded($event)">
    </TextView>
</ScrollView>

The purpose of this element is to act as a terminal, and is rapidly printing incoming messages from a bluetooth device.

Currently, the ScrollView is scrolling back to the top whenever I add some text to the terminalText variable, to which the TextView is bound. I would like to be able to keep the ScrollView at the bottom of the TextView.


A few notes:

I am adding text to the terminalText variable within my associated component class through this method:

public appendToTerminal(appendStr){
    this.terminalText += appendStr;
}

I have tried implementing the following code that would execute once the ScrollView loads:

private scrollIntervalId;
onScrollerLoaded(data: EventData){
    if(!this.scrollIntervalId){
        this.scrollIntervalId = setInterval(()=>{
            this.ngZone.run(() =>
                (data.object as any).scrollToVerticalOffset((data.object as any).scrollableHeight, false)
            )
        }, 10);
    }
}

(This attempt is based on an explanation given here

I have only tried this on an Android device, as I do not have access to an Apple device.

Instakat
  • 101
  • 2
  • 10

2 Answers2

9

you are setting TextView to fixed height 100% which is will be same as ScrollView that is why scrollableHeight will always be 0. you should use minHeight="100%".

then you can scroll programmatically to the end when you are appending text to terminal text this.terminalText += appendStr.

like this

public appendToTerminal(appendStr){
    this.terminalText += appendStr;
    setTimeout(()=>{
        scrollView.scrollToVerticalOffset(scrollView.scrollableHeight, false);

    },150);
}

this will append the text then get the scrollableHeight and then will scroll to it.

here is working playground demo:https://play.nativescript.org/?template=play-ng&id=Rs0xnP&v=16

bhavin jalodara
  • 1,470
  • 9
  • 12
  • 2
    Tried your method, still doesn't work. I may have found the reason why - I logged the values of scrollView and it's scrollableHeight and while scrollView is good, scrollableHeight is set to 0 at all times, even when I can manually scroll down the page. Any comment on this? I tried embedding the `TextView` in a `StackLayout`, but no luck there either. – Instakat Jul 06 '18 at 17:10
  • can you create the playground demo. then i can have a look. – bhavin jalodara Jul 06 '18 at 19:07
  • Playground Link: https://play.nativescript.org/?template=play-ng&id=Rs0xnP – Instakat Jul 06 '18 at 20:05
  • In case anyone get still 0 as scrollableHeight. I am not sure why, but without setTimeout it is not working. Handling the scrollable things in a setTimeout cb function solves the issue. – Gkiokan May 14 '19 at 07:53
  • you can also try running this in NgZone. – bhavin jalodara May 14 '19 at 09:18
  • in nativescript vue even settimeout doesn't help and scrollableHeight is still zero for me. – yukashima huksay Apr 25 '20 at 05:56
  • I have the same problem the difference is, that I am using a ListView as a child of the ScrollView, when I try to calculate the scrollableHeight always is 0. – Richard Aguirre May 03 '22 at 14:30
-1

The function below can only be used in the Angular ngDoCheck() or ngAfterContentChecked() lifecycle:

// See https://angular.io/guide/lifecycle-hooks
function goDownScrollView(scrollView: object, animate: boolean = true): boolean {

    let neScrollView:     ScrollView = <ScrollView>getNativeElement(scrollView),
        scrollableHeight: number     = neScrollView.scrollableHeight;

    console.log("neScrollView:", neScrollView);
    console.log("neScrollView scrollableHeight:", scrollableHeight);

    if (scrollableHeight > 0) {

        neScrollView.scrollToVerticalOffset(scrollableHeight, animate);
        return true;

    } else {

        return false;

    }

}

An helper to always get the native element:

function getNativeElement(object: object): object {

    return (object.hasOwnProperty("nativeElement")) ? object['nativeElement'] : object;

}

The scrollable height may be zero at the first pass of the lifecycle (for example, if you add elements to your ScrollView with an HTTP request). That's why you have to test the current content with new before scroll:

// See https://angular.io/api/core/ViewChild
@ViewChild("content") private _pageContent: ElementRef<ScrollView>;

public  currentContent:   object;
private _previousContent: object;

...

ngAfterContentChecked(): void {

    if (this.currentContent != this._previousContent) {

        let isScrollDown: boolean = goDownScrollView(this._pageContent);

        if (isScrollDown) {

            this._previousContent = this.currentContent;

        }

    }

}
bgrand-ch
  • 788
  • 1
  • 7
  • 19