NativeScript Angular ListPicker that behaves like `<select>` - nativescript

By default the ListPicker takes up a ton of screen space. Is there a way to make it behave like the metaphor for the HTML <select> when shown on mobile?
I've used this react native plugin before, and its exactly the metaphor I want, but for NativeScript.
Is this easy to do via NativeScript? I want to make use of the platform specific select metaphors, so showing/hiding a ListPicker or putting ListPicker in a modal is not what I'm looking for.
Also, I'm going to have a fairly long list, so an action Dialog wont work for me.
Update: I'm aware of nativescript-drop-down, however it does not use the platform specific "choose from list of choices" widget that webviews and react native plugin do.
By "platform specific choose from list of choices widget" I mean this (from https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select):
iOS (note picker renders where keyboard would, with rolodex picker):
Android (note modal, with scroll list of options):

I think you are looking for the nativescript-drop-down which is similar to the react-native-picker-select you had pointed.

I was looking for the same solution and couldn't find one so I have created my own. I have attached a sample for you here.
You can have a textField/Label and onTab you can show the ListPicker, like Select behaves in HTML and it will use the native(platform specific) components only.
in your HTML
<StackLayout orientation="vertical" width="210" height="210" backgroundColor="lightgray">
<Label text="Country" width="70" height="50" backgroundColor="red"></Label>
<TextField [(ngModel)]="textFieldValue" hint="Choose countty..." editable="false" (tap)="showHideField('country')"></TextField>
</StackLayout>
<StackLayout orientation="vertical" width="100%" height="210" *ngIf="showCountryPicker" backgroundColor="lightgray">
<ListPicker [items]="listPickerCountries" (selectedIndexChange)="selectedCountyChanged($event)"></ListPicker>
</StackLayout>
and your .ts file
showCountryPicker = false;
listPickerCountries: Array < string > = ["Australia", "Belgium", "Bulgaria", "Canada", "Switzerland",
"China", "Czech Republic", "Germany", "Spain", "Ethiopia", "Croatia", "Hungary",
"Italy", "Jamaica", "Romania", "Russia", "United States"
];
showHideField() {
this.showCountryPicker = true;
}
selectedCountyChanged(args) {
const picker = < ListPicker > args.object;
this.showCountryPicker = false;
this.textFieldValue = this.listPickerCountries[picker.selectedIndex];
}

Related

Svelte Native ListView - Horizontal

I try out and build my first App on Svelte Native. I develop this for an Tablet.
The standard ListView only supports vertical stacking.
I need this for horizontal.
This part only works for vertical:
<listView items="{listOfItems}" on:itemTap="{onItemTap}">
<Template let:item>
<!-- Shows the list item label in the default color and style. -->
<label text="{item}" />
</Template>
</listView>
<script>
import { Template } from 'svelte-native/components'
let listOfItems = ['one', 'two', 'three']
</script>
I also try this:
Here i have the Problem that nothing is showing.
{#each one as ones}
<button on:tap={addToList(ones)} text="{ones.name}" class="prod-btn" />
{/each}
So i need help to show a list of objects in an horizontal view.

Nativescript RadListView multiple selection: how to remove default style?

I'm using RadListView to create a multiple selection on IOS.
In front of the selection item somehow there is a circle checkbox I couldn't remove, which looks like:
My codes are:
<RadListView [items]="items" selectionBehavior="Press" multipleSelection="true" height="80%">
<ng-template let-item="item">
<Label [text]="item.name"></Label>
</ng-template>
</RadListView>
and:
this.items = [{ name: 'test1' }, { name: 'test2' }, { name: 'test3' }];
Anyone got an idea?
On iOS, those checkboxes appear beside your label is the only way user would distinguish between selected & unselected items.
You may apply padding only for iOS to prevent overlapping,
<RadListView [items]="items" selectionBehavior="Press" multipleSelection="true" height="80%">
<ng-template let-item="item">
<Label ios:paddingLeft="50" [text]="item.name"></Label>
</ng-template>
</RadListView>
If you still prefer a different style for selection, you will have to build your own, you may have a selected flag in each data item and toggle them upon tapping list item and update your CSS (may be background-color) for list item based on the selected flag.

How to Nest List Views in NativeScript

I'm trying to display a list of items within a list of items. Basically it's a card game, where each suit is being repeated, and then each card for each suit is being repeated.
<StackLayout margin="10 0 60 0" padding="0 0">
<ListView class="list-group" [items]="hand" (itemTap)="onItemTap($event)"
(itemLoading)="onItemLoading($event)" backgroundColor="#26252A"
style="height:100%">
<ng-template let-suit="item">
<FlexboxLayout flexDirection="row">
<ScrollView orientation="horizontal">
<StackLayout height="100" orientation="horizontal" margin="2.5, 15">
<ListView class="list-group" [items]="suit.cards">
<ng-template let-card="item">
<Label [text]="card" class="card"></Label>
</ng-template>
</ListView>
</StackLayout>
</ScrollView>
</FlexboxLayout>
</ng-template>
</ListView>
</StackLayout>
And this is what my "hand" looks like:
hand: { suit: Suit, fontColor: Color, cards: string[] }[] = [
{ suit: "Red", fontColor: new Color("red"), cards: ["14", "13"] },
{ suit: "Green", fontColor: new Color("green"), cards: ["14", "13", "12", "9"] },
{ suit: "Yellow", fontColor: new Color("yellow"), cards: ["14", "13", "10", "9"] },
{ suit: "Black", fontColor: new Color("black"), cards: ["14", "13", "9"] }
];
When I run it though, I'm only getting the very first card in each suit to be displayed.
You can check it out at the playground here:
https://play.nativescript.org/?template=play-ng&id=XfogFt&v=3
(I'm new to both NaitiveScript and Angular, so I may be missing something simple)
EDIT: It is not advisable to use nested listview as this would break
the recycling and virtualization for cells that are containing a
nested listview
You don't need a scrollview inside the ng-template, if you just remove it, it will show your all the items in each row of parent list.
<ListView class="list-group" [items]="hand" (itemTap)="onItemTap($event)"
(itemLoading)="onItemLoading($event)" backgroundColor="#26252A"
style="height:100%">
<ng-template let-suit="item">
<FlexboxLayout flexDirection="row">
<!-- <ScrollView orientation="horizontal"> -->
<StackLayout height="100" orientation="horizontal" margin="2.5, 15">
<ListView class="list-group" [items]="suit.cards">
<ng-template let-card="item">
<Label [text]="card" class="card"></Label>
</ng-template>
</ListView>
</StackLayout>
<!-- </ScrollView> -->
<Label text="Label"></Label>
</FlexboxLayout>
</ng-template>
</ListView>
I have updated the playground here. You can also use the itemHeight and itemWidth properties here for size tuning.
P.S. The itemHeight and itemWidth properties are iOS specific. If not used, items are sized dynamically depending on the data coming from the source.
As #Narendra mentioned it's not recommended to use nested list views or ngFor inside template.
I guess nativescript-accordion plugin will suit your needs, it basically supports the data structure you are looking for - List Item -> List of Items (Suit -> Cards). If you like to show the items expanded upon loading, all you have to do is, populate the selectedIndexes with all indexes. There is one issue with latest version of the plugin, which can be still handled with a simple math.
Preventing collapse upon tap is still an open feature request, but possible to achieve with an override. But as far I know, this plugin could be the only viable solution for nested list views.

How to show a button if there are no items in a ListView?

I have the following structure:
<ScrollView tkMainContent>
<ListView [items]="students$ | async" class="list-group" *ngIf="students$">
<ng-template let-student="item">
<StackLayout>Student details go here</StackLayout>
I'm not able to show a button inside the ScrollView when there is no student in my list.
How can I still show the button?
Note: I'm testing on a real iOS device.
<FlexboxLayout flexDirection="column">
<GridLayout class="page-content" id="placeholderLayout" visibility="{{ hasContent ? 'collapse' : 'visible' }}">
<Label class="page-icon fa" text=""></Label>
<Label class="page-placeholder" style="white-space: normal" text="Click the camera button to add image"></Label>
</GridLayout>
<ScrollView>
<-- List View Here -->
</ScrollView>
</FlexboxLayout>
I use something like this on NS Core, to show placeholder content. The way to set visibility might be different in angular, but a similar markup should work for you.
In the component.ts, you should take care to evaluate if there is content to show in list view, if there are, then set hasContent to true, and false otherwise.
Hope that helps :) let me know if you face any trouble while implementing this.

How to add an activity-indicator on a page level?

I would like to add an activity-indicator widget in my login page but I would like it to cover the whole screen, so I can prevent double click on the Login button.
Any idea, thanks!
If you wrap everything in a GridLayout, add a StackLayout as the last item in the row you want to cover. The StackLayout by default will cover the whole screen. Then you can show/hide via data. For example:
<GridLayout>
<StackLayout>
// All your page content goes here!
</StackLayout>
<StackLayout class="dimmer" visibility="{{showLoading ? 'visible' : 'collapsed'}}"/>
<GridLayout rows="*" visibility="{{showLoading ? 'visible' : 'collapsed'}}">
<ActivityIndicator busy="true" />
</GridLayout>
</GridLayout>
I have a "dimmer" StackLayout that I animate to be semi transparent black, then the Activity Indicator sits on top.
not sure what layout you have i will only put example(somehow simplified) from my project
Inside page u can put something like this, both StackLayout and ActivityIndicator are inside GridLayout which takes whole size of page
<GridLayout rows="*" columns="*">
<StackLayout visibility="{{ showLogin ? 'visible' : 'collapse'}}" row="0" column="0">
<!--Login form, as you have defined-->
</StackLayout>
<!--Indicator on whole page, colSpan and rowSpan force ActivityIndicator to takes whole page-->
<ActivityIndicator visibility="{{ !showLogin ? 'visible' : 'collapse'}}" busy="{{ !showLogin }}" rowSpan="1" colSpan="1" row="0" column="0" />
</GridLayout>
And inside javascript code
/*somehow add showLogin property to bindingContext*/
page.bindingContext.set("showLogin",false) //false for show ActivityIndicator
/*or*/
page.bindingContext.set("showLogin",true) //true for show form
But best would be to put to already defined Observable which you should have assigned to bindingContext
So based on showLogin property u will get visible either ActivityIndicator(on whole page) or form
Not sure if i forgot something but if something, write comment :)
The activity indicator on its own won’t prevent dual submissions of your forms. In addition to displaying an ActivityIndicator, you should also set the isEnabled flag on your Button UI components to false during the submission. For example:
<!-- template -->
<Button [isEnabled]="!isAuthenticating" (tap)="submit()"></Button>
// JavaScript/TypeScript
export class LoginComponent {
isAuthenticating = false;
submit() {
this.isAuthenticating = true;
doTheActualLogin()
.then(() => {
this.isAuthenticating = false;
});
}
}
You can find a complete implementation of a login that prevents dual submissions and uses an ActivityIndicator in the NativeScript Groceries sample. Take a look at how the isAuthenticating flag is used in this login folder for the specific implementation.

Resources