StackLayout with ngFor inside ScrollView - nativescript

I'm trying to build an scrollable list by using a StackLayout with an ngFor (and a ScrollView, of course).
This is my code:
<StackLayout class="home-panel" verticalAlignment="top">
<StackLayout orientation="horizontal">
<!--Suggest Append SuggetAppend -->
<RadAutoCompleteTextView #autocmp [items]="items" suggestMode="Suggest" displayMode="Plain" width="80%">
<SuggestionView tkAutoCompleteSuggestionView suggestionViewHeight="300">
<ng-template tkSuggestionItemTemplate let-item="item">
<StackLayout orientation="vertical" padding="10">
<Label [text]="item.text"></Label>
</StackLayout>
</ng-template>
</SuggestionView>
</RadAutoCompleteTextView>
<Button text="Add" (tap)="onAdd()" width="20%"></Button>
</StackLayout>
<ScrollView>
<StackLayout *ngFor="let item of this.shopList">
<Label text="{{item}}" (tap)="itemSelected(item)" fontSize="36"></Label>
</StackLayout>
</ScrollView>
</StackLayout>
The problem arises with the ScrollView at the end of the main StackLayout, which apparently shows only the last element in the shoppingList. The functionality I want to have is a text box on top (with an 'add' button on the same line), and a scrollable list filling the rest of the screen.

You have to wrap your StackLayout with the *ngFor into another Layout container, so the ScrollView can calculate the height.
...
<ScrollView>
<StackLayout>
<StackLayout *ngFor="let item of this.shopList">
<Label text="{{item}}" fontSize="36"></Label>
</StackLayout>
</StackLayout>
</ScrollView>
...

Related

How to vertically fill space between elements in ScrollView while preserving scrolling

I am trying to achieve the following:
I tried achieving this with a GridLayout and only get one of the following two things working:
The space between the Label and the StackLayout is filled (label is
stretched), but scrolling will not work when adding more labels into
the StackLayout
Scrolling will work when more labels are added into
the StackLayout, but when only one label is showed for example, the
label before the StackLayout will not stretch
Is there any way to achieve such a thing? GridLayout is not necessarily needed, but I tried several ways and could not find any way of doing this.
example code of (1)
<Page class="page dark">
<ScrollView backgroundColor="red">
<GridLayout rows="*, auto" height="100%" backgroundColor="blue">
<Label backgroundColor="green">Top Label</Label>
<StackLayout row="1" backgroundColor="green">
<Label backgroundColor="purple">Label A</Label>
<Label backgroundColor="purple">Label B</Label>
</StackLayout>
</GridLayout>
</ScrollView>
</Page>
example code of (2):
<Page class="page dark">
<ScrollView backgroundColor="red">
<GridLayout rows="*, auto" backgroundColor="blue">
<Label backgroundColor="green">Top Label</Label>
<StackLayout row="1" backgroundColor="green">
<Label backgroundColor="purple">Label A</Label>
<Label backgroundColor="purple">Label B</Label>
</StackLayout>
</GridLayout>
</ScrollView>
</Page>
I wanted to achieve this for months and I finally found a working solution, for iOS and Android.
The solution is to use FlexboxLayout instead of GridLayout. When placed in a ScrollView, FlexboxLayout will take the whole available height if it is not tall enough. If it is taller than the ScrollView, it will be scrollable.
Here is the solution for your example:
<Page class="page dark">
<ScrollView backgroundColor="red">
<FlexboxLayout
flexDirection="column"
justifyContent="space-between"
alignItems="stretch"
backgroundColor="blue"
>
<Label backgroundColor="green">Top Label</Label>
<StackLayout backgroundColor="green">
<Label backgroundColor="purple">Label A</Label>
<Label backgroundColor="purple">Label B</Label>
</StackLayout>
</FlexboxLayout>
</ScrollView>
</Page>

StackLayout's vertical alignment

I'm trying to put a button at the bottom of a StackLayout and it's not working.
I don't know what I'm doing wrong!
Here's my template:
<Page actionBarHidden="true">
<GridLayout rows="*, *, *, *" columns="*">
<StackLayout horizontalAlignment="center"
verticalAlignment="center">
<Label text="My Account" id="login-label"/>
</StackLayout>
<CardView row="1" class="cardStyle" margin="10"
elevation="40"
radius="4"
verticalAlignment="center"
horizontalAlignment="center"
rowSpan="2"
id="login-box">
<StackLayout id="form-container">
<TextField hint="Login"/>
<TextField hint="Password"/>
<Label text="Forgot password?" horizontalAlignment="right"/>
<Button text="Button" #tap="loginButton()" verticalAlignment="bottom"/>
</StackLayout>
</CardView>
</GridLayout>
</Page>
Thank you!
StackLayout do not support that by its design. It is used to just stack the child elements one after another in given orientation, you can't have mixed output - few child elements at top and few at bottom or center.
Use Grid / Dock layout in order to dock an element at bottom.

StackLayout ignoring EndAndExpand on iOS

It seems that there is a confirmed Bug in Xamarin.Forms in iOS, when you have a content/label with a text too large that needs 2 lines to display it in a StackLayout (lets say layoutA), the others siblings and parent elements takes the width of the layoutA.
Kent Boogaart filled a Bug with the following example to reproduce the error.
<StackLayout Margin="0,20,0,0">
<!-- works (by virtue of no wrapping) -->
<StackLayout Orientation="Horizontal">
<Switch/>
<Label Text="Some text"/>
<Button Text="A Button" HorizontalOptions="EndAndExpand"/>
</StackLayout>
<!-- works (by virtue of no switch) -->
<StackLayout Orientation="Horizontal">
<Label Text="Some longer text that wraps over two lines"/>
<Button Text="A Button" HorizontalOptions="EndAndExpand"/>
</StackLayout>
<!-- does not work -->
<StackLayout Orientation="Horizontal">
<Switch/>
<Label Text="Some longer text that wraps over two lines"/>
<Button Text="A Button" HorizontalOptions="EndAndExpand"/>
</StackLayout>
</StackLayout>
Is there a solution/fix/workaround for this problem?
EDIT
Here is the code used:
<ListView HasUnevenRows="True" SeparatorVisibility="None" HorizontalOptions="StartAndExpand" CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="#eee" Padding="5" Margin="5" >
<ffimageloading:CachedImage HorizontalOptions="Start" VerticalOptions="Start" HeightRequest="75" Source="{Binding ID, StringFormat='https://example/api/{0}'}" CacheDuration="15">
<ffimageloading:CachedImage.Transformations>
<fftransformations:CircleTransformation/>
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand">
<Label Text="{Binding Name}" TextColor="#f35e20" HorizontalOptions="FillAndExpand"/>
<Label Text="{Binding Description}" HorizontalOptions="FillAndExpand" TextColor="#503026" />
<Label Text="{Binding ID}" x:Name="ID" IsVisible="False"></Label>
<Button Text="Ver productos" TextColor="#FF5F6D" HorizontalOptions="End" BackgroundColor="Transparent"></Button>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
These are actual screenshots of the app in iOS, in Android it just work great.
As you can see, in the Image1 the first card is OK, the button ("Agregar al carrito" or "Ver Productos") is at the end.
But in the second card as the text is too large, the button gets down out of the card.
And in the second image, the text in the middle needs 2 lines, and the StackLayout that wraps the content (the 2 labels and the button) gets just as wide as the label with the large text.
I draw a red square in the large text, and a red line so you can see that because of that large text, the StackLayout gets narrow.
This is a known issue that when setting the padding on stacklayout inside Cell , the row height displays incorrectly.
Solution: Use StackLayout to wrap the content under the Cell.
<ViewCell>
<StackLayout > //add this
<StackLayout Orientation="Horizontal" Padding="5" Margin="5" >
</ffimageloading:CachedImage>
<StackLayout BackgroundColor="Brown" Orientation="Vertical">
<Label TextColor="#f35e20" HorizontalOptions="FillAndExpand"/>
<Label/>
<Label Text="fff" x:Name="ID" IsVisible="False"></Label>
<Button Text="Ver productos" TextColor="#FF5F6D" BackgroundColor="Yellow"></Button>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>

How to customize tabview in nativescript?

How can i dock my Tabview items to the left of the screen like in the image below?
This is how my layout looks like currently.
<TabView dock="left" tabsBackgroundColor="red" selectedIndex="1" selectedColor="#FF0000" iosIconRenderingMode="alwaysOriginal" sdkExampleTitle sdkToggleNavButton>
<StackLayout tabsBackgroundColor="red" *tabItem="{ iconSource: 'res://ic_ham'}" >
</StackLayout>
<StackLayout *tabItem="{title: 'Home'}">
<ns-home></ns-home>
</StackLayout>
<StackLayout *tabItem="{title: 'Bookings'}">
<ns-booking></ns-booking>
</StackLayout>
</TabView>
Resulting layout
It may be a bit of overkill for your needs, but what I've found simplest for us was to change the tab buttons to be fully designable and customizable by disabling the current buttons and the add new tab buttons.
<StackLayout class="grid-tab-view" columns="*,100,100,100,*" ios:rows="auto, auto" android:rows="auto, *">
<label row="0" col="1" class="tab-button" text="Tab1" (tap)="switchTabByIndex(0)" [ngClass]="{'selected': tabSelectedIndex===0}"></label>
<label row="0" col="2" class="tab-button" text="Tab2" (tap)="switchTabByIndex(1)" [ngClass]="{'selected': tabSelectedIndex===1}"></label>
<label row="0" col="3" class="tab-button" text="Tab3" (tap)="switchTabByIndex(2)" [ngClass]="{'selected': tabSelectedIndex===2}"></label>
<TabView colSpan="5" row="1" col="0" #tabView class="tab-view" [(ngModel)]="tabSelectedIndex" (loaded)="onTabsLoaded()" (selectedIndexChanged)="onTabSwitch($event)">
<StackLayout class="tab" *tabItem="{title: 'Tab1'}">
<Label text="tab1 body"></Label>
</StackLayout>
<StackLayout class="tab" *tabItem="{title: 'Tab2'}">
<Label text="tab2 body"></Label>
</StackLayout>
<StackLayout class="tab" *tabItem="{title: 'Tab3'}">
<Label text="tab3 body"></Label>
</StackLayout>
</TabView>
</StackLayout>
And in code:
Add tap event handlers for when button selected and give it a custom selected class (for styling)
Hide the default tab buttons using the tabs loaded event:
onTabsLoaded(): void{
let tabViewElement = <TabView>this.tabView.nativeElement;
if (tabViewElement && tabViewElement.android) {
tabViewElement.android.removeViewAt(0);
} else {
tabViewElement.ios.tabBar.hidden = true;
}
};
and with a bit of css, our result:
Hope this helps, good luck!

why Listview is not scroll-able inside scrollview in nativescrit angular2

# I am using listview inside the Scrollview but listview is not scrollable inside the scrollview as top parent view
component.html
<ScrollView>
<StackLayout class="zindex">
<!--<AutoComplete [items]="ArrayVillage" (itemTap)="itemTapped($event)"> </AutoComplete>-->
<SearchBar row="0" #sb hint="Search for a country and press enter" (clear)="onClear()" [text]="searchPhrase" (submit)="onSubmit(sb.text)"></SearchBar>
<ListView row="1" [items]="myItems" class="list-group">
<template let-item="item">
<GridLayout class="item" class="list-group-item">
<Label [text]="item.name" class="list-group-item-heading"></Label>
</GridLayout>
</template>
</ListView>
</StackLayout>
<ScrollView>
Try Using Repeater instead of ListView as mentioned in https://docs.nativescript.org/cookbook/ui/repeater. Following is an example of how you can include Repeater inside a ScrollView and can obtain whole layout as scrollable.
<ScrollView>
<StackLayout class="margin">
<Label text="{{ description }}"></Label>
<Repeater items="{{ options }}" row="1">
<Repeater.itemTemplate>
<StackLayout>
<Label text="{{ description }}"></Label>
</StackLayout>
</Repeater.itemTemplate>
</Repeater>
</StackLayout>
</ScrollView>
Trying it by wrapping the scrollview into a AbsoluteLayout
<AbsoluteLayout>
<ScrollView>
<StackLayout class="zindex">
//Rest of stuff
</StackLayout>
<ScrollView>
</AbsoluteLayout>
Having a scrollview inside other one is not a good practice. It doesn't work beacuse scrollview tries to calculate the infinite view and do it with a scrollable inside could work just in some cases having control of the parent view, but don't go that painfull way.
In your code I have a question, why would you want to scroll the SearchBar? Try this structure I think is what you want.
<StackLayout>
<SearchBar></SearchBar>
<ListView>
<template>
<GridLayout >
<Label ></Label>
</GridLayout>
</template>
</ListView>
The SearchBar is fixed and the List scrolllable
Look at the video, play with the emulator and you will see that at the beggining it seems to work but is the "computer mouse scroll" when you use click trying to scroll it doesn't work anymore and is because the screen doesn't know which scrollable element has to scroll.
To do 2 scrollable parts can be a solution, as shown in the end of the video (I implemented in Nativescript with Javascript because I'm working in a project but is almost the same)
<StackLayout>
<Label fontSize="20" color="blue" text="You can do two scrollable parts" textWrap="true" />
<Button text="to" />
<ListView items="{{ items }}" height="300" loaded="onLoaded" itemLoading="onItemLoading" itemTap="onItemTap">
<ListView.itemTemplate>
<Label text="{{ name }}" textWrap="true" />
</ListView.itemTemplate>
</ListView>
<ScrollView>
<StackLayout>
<Button text="1" />
<Button text="2" />
<Button text="3" />
<Button text="4" />
<Button text="5" />
<Button text="6" />
<Button text="3" />
<Button text="4" />
<Button text="5" />
<Button text="6" />
</StackLayout>
</ScrollView>
</StackLayout>

Resources