Nativescript: Need to set Listview height with total height of its items

Created on 3 Aug 2016  路  25Comments  路  Source: NativeScript/NativeScript

Hi,

In my application i need to show bunch of datas (which goes beyond the screen) and bottom of all of this i need to put listview to show some items (items height differ with one another).
because of this i put scrollview in my page and put everything in my scrollview.This allows me to scroll my data but my listview's height automaticly set to first items height and i end up with very little listview with second scroll.If my items height was same i can use something like but i cant.
Is there any way to set ListViews height with its total height of items or make listview to expand its height automaticly.

Thanks.

question

Most helpful comment

@tomylee001001
Basic usage of *ngFor in N+Angular-2 app

ngfor.component.html


<StackLayout *ngFor="let item of items">
    <Label [text]="item"></Label>
</StackLayout>

ngfor.component.ts

import { Component } from "@angular/core";

var dataItems = ["data-item 1", "data-item 2", "data-item 3"]

@Component({
    selector: 'ngfor-component',
    templateUrl: "./ngfor.component.html",
})

export class NgForComponent {
    public items:Array<string> = [];

    constructor(){
        this.items = dataItems;
    }
}

All 25 comments

Hi @tomylee001001

It will help us if you provide a sample code in order to reproduce your scenario and give the best advice.
Meanwhile, you can set your ListView height and rowHeight.
The first property will pre-define you entire ListView height.
rowHeight will pre-define each of your ListView item's cells height.

e.g. (in your case as your cells are different size you probably don't need rowHeight)

  <ScrollView>
      <ListView items="{{ source }}" height="500" rowHeight="50"">
          <ListView.itemTemplate>
              <Label text="{{ $value }}" textWrap="true" />  
          </ListView.itemTemplate>
      </ListView>
  </ScrollView>

Note: Not recommended to nest ListView inside a ScrollView as it will create two elements using a scroll - as a result this will slow down your app and also confuse the user.

Hi,
i know that i can set height of listview directly like height="500" or programmatically like [height]="myheight" but how can i get total height of my listview items so that i can set that height to my listview. i try using the "setupItemView" events SetupItemViewArgs.view to get each items height but i coudnt do it.
My gesign code is

`

  <StackLayout>
      <GridLayout columns="45,*" verticalAlignment="center" class="post-user"  >
          <Image #user_pic src="{{ board_main.user.pic_url }}" col="0" class="user-pic"></Image>
          <GridLayout rows="auto,45"  col="1" verticalAlignment="center">
              <GridLayout columns="80,*"  row="0">
                  <Label [text]="board_main.user.title" class="medium-spacing" col="0"></Label>
                  <Label [text]="board_main.user.name" class="medium-spacing"  col="1"></Label>
              </GridLayout>
              <Label [text]="board_main.share_date" class="medium-spacing" row="1" class="date-str"></Label>
          </GridLayout>
    </GridLayout>
    <GridLayout rows="*,45"   >
        <HtmlView [html]="board_main.content" row="0"></HtmlView>
        <GridLayout columns="auto" row="1" class="font-small">
            <Label [text]="board_main.comment_amount" class="medium-spacing" col="0"></Label>

        </GridLayout>
    </GridLayout>
    <ListView [items]="board_main.attachments"  width="100%" [height]="board_main.rows"  class="small-spacing" [class.visible]="listLoaded"   >
        <template let-item_="item"  let-i_="index">
            <GridLayout columns="*" rows="80">
                <Image src="{{ item_.pic_uri }}" width="150" row="1"  col="0" (tap)="paylasimgit(item_)"></Image>
            </GridLayout>
        </template>
    </ListView> 
<ListView #comments [items]="board_List" class="small-spacing" [class.visible]="listLoaded"  >
<template let-item="item"  let-i="index">
  <StackLayout>
      <GridLayout columns="45,*" verticalAlignment="center" class="post-user" >
          <Image #user_pic src="{{ item.user.pic_url }}" col="0" class="user-pic"></Image>
          <GridLayout rows="auto,45"  col="1" verticalAlignment="center">
              <GridLayout columns="80,*"  row="0">
                  <Label [text]="item.user.title" class="medium-spacing" col="0"></Label>
                  <Label [text]="item.user.name" class="medium-spacing"  col="1"></Label>
              </GridLayout>
              <Label [text]="item.share_date" class="medium-spacing" row="1" class="date-str"></Label>
          </GridLayout>
    </GridLayout>
    <GridLayout rows="*">
        <HtmlView [html]="item.content" row="0"></HtmlView>
    </GridLayout>
    <ListView [items]="item.attachments"  width="100%" [height]="item.rows"  class="small-spacing" [class.visible]="listLoaded" >
        <template let-item_="item"  let-i_="index">
            <GridLayout columns="*" rows="80">
                <Image src="{{ item_.pic_uri }}" width="150" row="1"  col="0" (tap)="paylasimgit(item_)"></Image>
            </GridLayout>
        </template>
    </ListView> 
  </StackLayout>
</template>


</ScrollView>`

Thanks for helping.

@tomylee001001

Try using another layout for your container instead of StackLayout - e.g GridLayout with row that has * to take the whole existing space where your list-view will be rendered (if we are talking about the inner list-view that is wrapped in a stack - based on your code I am not quite sure which list-view you are trying to control )

hi,
i did try the gridlayout but it didnt change anything either.is there realy no way to get listviews items height or prevent it from collapsing to its first row or make it expend to its full height beyond screen?
i deleted the unnecessary codes to make it a bit simpler.


<ScrollView orientation="vertical">
    <StackLayout>
        <GridLayout rows="*,45"   >
            <HtmlView [html]="board_main.content" row="0"></HtmlView>
            <GridLayout columns="auto" row="1" class="font-small">
                <Label [text]="board_main.comment_amount" class="medium-spacing" col="0"></Label>
            </GridLayout>
        </GridLayout>
        <ListView #comments [items]="board_List" class="small-spacing"  >  -->>This is the listview that i wanted to expand to its total items height <--
            <template let-item="item"  let-i="index">
                  <StackLayout>
                    <GridLayout rows="*">
                        <HtmlView [html]="item.content" row="0"></HtmlView>
                    </GridLayout>
                </StackLayout>
            </template>
        </ListView>
   </StackLayout>
</ScrollView>

@tomylee001001

You can expand your list-view using a container grid with * for the row that will hold the listview BUT your list-view should not be wrapped in scroll-view as 1.) it already has scroll 2.) scroll-view does not have pre-defined size and will expand to its first child height unless a height is passed.
Another applicable option is to use *ngFor instead of list-view.

For solution 1 here is a sample code taht I have used:
_page.html_

<!-- parent rid to exapnd all the usable space with rows="*" and columns="*"-->
<!-- note the the second row is * (this will hold the list-view)-->
<GridLayout rows="auto, *" columns="*"> 

    <ScrollView row="0" orientation="vertical">
        <GridLayout rows="*,45"   >
            <HtmlView [html]="htmlString" row="0"></HtmlView>
            <GridLayout columns="auto" row="1" class="font-small">
                <Label text="label in grid text" class="medium-spacing" col="0"></Label>
            </GridLayout>
        </GridLayout>
    </ScrollView>

    <ListView row="1" [items]="items" > 
        <template let-item="item" let-i="index">
                    <HtmlView [html]="htmlString"></HtmlView>
        </template>
    </ListView>
</GridLayout>

_page.ts_ (if you wan't to test the example locally)

import { Component, ChangeDetectionStrategy, Input }  from "@angular/core";

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
    public htmlString: string = '<div style="background-color:blue; color:white">My Text Here</div>';
    public items: Array<string>;

    constructor() {
        this.items = ["commnet 1", "comment 2", "comment 3"];
    }
}

Hi,
i try your suggestion but this doesnt work for me.1)if the first portion of the code goes beyond the screen (which is for me for the most parts) i cant reach the listview with scrollviews scroll.2)if the first portion of the code doesnt go beyond the screen even if i can see the listview i still had to scroll the listview while my first portion of code stay above.
What my aim is i want to scroll the page as whole.Like you said if i can set the listview height my problem will solve.thats why i was asking if is there anyway to set listviews height as total height of its items.if there is realy no way to find the listview items height or find out the listviews innerheight it will be realy problematic for me .As for the "*ngFor" can you give any link for its usage.i saw it in some examples but there was no explanation to how to use it.i am using nativescript-angular in my app.

Thanks for helping.

@tomylee001001 I've been watching your thread here and was going to mention something about ngFor before as I think it's much better suited to what you're trying to accomplish. I wasn't sure if you were using Angular or not. I want to mention that I have an issue open about the lack of documentation on ngFor here. Maybe you could go there and add a comment about it.

@tomylee001001
Basic usage of *ngFor in N+Angular-2 app

ngfor.component.html


<StackLayout *ngFor="let item of items">
    <Label [text]="item"></Label>
</StackLayout>

ngfor.component.ts

import { Component } from "@angular/core";

var dataItems = ["data-item 1", "data-item 2", "data-item 3"]

@Component({
    selector: 'ngfor-component',
    templateUrl: "./ngfor.component.html",
})

export class NgForComponent {
    public items:Array<string> = [];

    constructor(){
        this.items = dataItems;
    }
}

Thanks a lot for your help.
After changing the listview to *ngfor it work just like i wanted.
Can i also ask what is the difference between listview and *ngfor?
How should i choose listview or *ngfor?

@tomylee001001
ListView is NativeScript Core component - it has some specific build in methods and is widely used in a plain NativeScript project. Using ListView will allow you to be as native as possible as the code behind this module is referring to android.widget.ListView in Android and to UITableView in iOS.
Therefore, using ListView will make your app experience truly native (in meanings of mobile development)

*ngFor is a structural directive that comes from Angular-2 so it can be used only when you are developing NativeScript + Angular-2 project.

Both are used to display list patterns of data - the choice when to use ngFor is strictly yours but overall use *ngFor in Angular-2 projects if you want customized UX or if you don't the build in functionalities of ListView. Or in the cases like the one above :)

Thanks for your help.

@tomylee001001 I think @NickIliev missed a couple important points. The ListView is virtualized which means that it can handle thousands of rows. It only creates GUI elements for whats inside the display area, and then changes out those elements when you scroll to simulate scrolling through a long list. Virtualization results in lists loading faster when you have hundreds or thousands of elements. It can also result in some lag or delay when you scroll, but that's true of virtualized lists on any platform. *ngFor is not virtuliazed unless you implement this behavior yourself or use someone else's component. I see there's a virtualized ngFor project on GitHub but I don't know if there's anyway to make this work with NativeScript.

@NickIliev correct me if I'm wrong on this, but another important distinction is that the ListView doesn't handle parent/child scenarios, or in other words, grouping or nested lists. Say you have a list of businesses and anywhere from 1 to 1000 contacts under each company/business, and you'd like to display them visually something like this in your app:

Company A
   John Doe
   Sally Ann Doe
Telerick
   Nick Iliev
   Georgi Krustev
   TJ VanToll

This is easy to do with Angular as you just nest your repeaters, in this case *ngFor. But you currently can't nest ListViews. See issue #92.

@harlankoehn You can nest listviews in Nativescript+angular project. if you look at my desing code i post earlier i'm nesting listviews and it works. i'm using several nested listviews in my current project.only problem i have face with nested listviews you need to set height for your child listview so that it doesnt collapse on its first item.if it collapse its first item there will be scroll issues between the main listview scroll and child listview. Also you can use *ngFor inside the listview to get your desire effect.

tomylee001001 Can you post finished example? I can't make it work. ;/

Working Example( I had trouble understanding it at first ):

 51         <GridLayout row="2" rows="auto auto" class="invite-to-jobs-container">
 52           <Label row="0" col="0" horizontalAlignment="left" verticalAlignment="top" class="fa briefcase" text="&#xf0b1;"></Label>
 53           <Label row="0" col="1" horizontalAlignment="left" verticalAlignment="top" class="hire-title" text="Hire for a Job"></Label>
 54 
 55           <StackLayout row="1" colSpan="2" class="job-row-container">
 56             <GridLayout *ngFor="let item of jobService.jobs$ | async">
 57               <GridLayout>
 58                 <Label col="0" [text]="item.title" horizontalAlignment="left" verticalAlignment="center" class="job-title"></Label>
 59                 <Button col="1" class="invite-to-job-button" horizontalAlignment="right" (tap)="inviteToJob(item.title, item.description)" text="Invite"></Button>
 60               </GridLayout>
 61             </GridLayout>
 62           </StackLayout>
 63         </GridLayout>

We are using listview only for that ripple effect. And now you are suggesting us to go for gridlayout and it doesnt serve the purpose.

Did you ever come up with a solution to have ListView's automatically figure out their own height (based on their contents) ? @rajasekarsp

There must be an easier way... *ngFor hinders performance quite a lot.

Yes right @MarkPieszak .

@NickIliev You have any solution? Not workaround.

@rajasekarsp @MarkPieszak

You can get the height of your ListView using the following (Angular)

HTML file

<ListView [items]="items" (loaded)="onListLoaded($event)" class="list-group">

component file

onListLoaded(args) {
    let list = <ListView>args.object;

    setTimeout(() => {
        console.log("actual height size: " + list.getActualSize().height);
        console.log("getMeasuredHeight : " + list.getMeasuredHeight());
    }, 300);
}

thanks @NickIliev for the tips! haha

Using NS/Angular the onListLoaded list returns:

  • getActualSize() return with width: 0 and height: 0
  • getMeasuredHeight() with 0 as well.

Make sure you don't wrap any ListView component in a ScrollView or it won't render the list to its full height. It will only render 1 unit tall if you do. It must be some kind of bug.

@IAMtheIAM yeah, I'm rendering inside a ScrollView, because the screen needs to scroll another content below the StackLayout containing the ListView.

Anyway, the ListView needs to resize, using or not a ScrollView like Android does it.

- This is a kind of bug, yeah -

I opened this ticket

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NickIliev picture NickIliev  路  58Comments

ikhsan017 picture ikhsan017  路  55Comments

morningrat picture morningrat  路  67Comments

lscown picture lscown  路  58Comments

valentinstoychev picture valentinstoychev  路  136Comments