Nativescript Listview with header and scroll entire page - nativescript

I am using ListView with Header portion on top of it like below,
<StackLayout>
<StackLayout height="200">
<Label text="Header content goes in this section"></Label>
<StackLayout>
<ListView [items]='posts'>
<!-- template items goes here -->
</ListView>
</StackLayout>
When we scroll to list the header is sticky in this case.
Is there a option that scroll overrides header also ?.I mean that header also part of scroll.

Fr Angular-2 application you can now use tkTemplateKey deirective and create your own headers, footers, groups and other custom list-view elements.
Example can be found here
Here is the code for a list-view with header and groups.
page.component.html
<ListView [items]="countries" [itemTemplateSelector]="templateSelector" (itemTap)="onItemTapFirstList($event)" class="list-group" separatorColor="white">
<ng-template nsTemplateKey="header" let-header="item">
<Label [text]="header.name" class="list-group-item h3 bg-primary" isUserInteractionEnabled="false" color="white" fontSize="24"></Label>
</ng-template>
<ng-template nsTemplateKey="footer" let-footer="item">
<Label [text]="footer.name" class="list-group-item" backgroundColor="gray"></Label>
</ng-template>
<ng-template nsTemplateKey="cell" let-country="item">
<StackLayout class="list-group-item">
<Label [text]="country.name" class="list-group-item-heading"></Label>
<Label [text]="country.desc" class="list-group-item-text" textWrap="true"></Label>
</StackLayout>
</ng-template>
</ListView>
page.component.ts
#Component({
moduleId: module.id,
templateUrl: "./multi-line-grouped.component.html",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MultiLineGroupedListViewExampleComponent implements OnInit {
public countries: Array<any> = [];
public templateSelector = (item: any, index: number, items: any) => {
return item.type || "cell";
}
ngOnInit() {
for (let i = 0; i < mockedCounties.length; i++) {
this.countries.push(mockedCounties[i]);
}
}
onItemTapFirstList(args: ItemEventData) {
console.log(args.index);
}
}

Not sure if there's another way, but one way could be moving the header inside the listview. For that to work it needs to be in the posts Array, so you may want to transform that into some sort of wrapping class that can contain eiter a header or item row. Then create two templates inside the listview that depending on the template key render a header or an item.
For details on templates, see https://docs.nativescript.org/cookbook/ui/list-view#define-multiple-item-templates-and-an-item-template-selector-in-xml

You can use *ngFor creating the list.Here is the sample code for doing this.
<ScrollView>
<StackLayout>
//define your header over here
<Label text="hey header"></Label>
<StackLayout *ngFor="let item of <Array>">
<GridLayout columns="4*,*" rows="*,">
<Label row="0" col="0" text="hey label"></Label>
</GridLayout>
<StackLayout>
<StackLayout>
</ScollView>

Related

NativeScript - Change Label into TextView/TextField with a Button

I am learning to make an app in NativeScript (Angular 2). In my item page, I want to have a button so that when I press it, I can change Label into TextView/TextField for editing the information of the item.
I know that I can use editable in TextView but I still want to know if it is feasible to have the button with that functionality. Thank you !!
item.component.html:
<StackLayout>
<Label class="h3" text="Name: {{ item.get_name() }}" textWrap="true">
</Label>
<Label class="h3" text="Credit: {{ item.get_credit() }}"></Label>
<Button class="btn" text="Edit" (tap)="change()"></Button>
</StackLayout>
<!-- After pressing the button -->
<StackLayout>
<TextView class="h3" [text]="item.get_name()" textWrap="true">
</TextView>
<TextView class="h3" [text]="item.get_credit()"></TextView>
<Button class="btn" text="Save" (tap)="change()"></Button>
</StackLayout>
This can be done in many ways, but one common way is by changing visibility of control and binding it to a variable / property in the code behind.
in your component html:
Then on your component ts or code-behind you can handle it in the change method:
class MyComponentSample {
isLabelMode: boolean = true; // Set to true if you want label to show by default or false if TextView as default
change() {
this.isLabelMode = !isLabelMode; // Basically you are toggling the mode here.
}
}

How to create a route based BottomNavigation component?

BottomNavigation component requires to put all tab contents on same page.
e.g:
<TabContentItem>
<GridLayout>
<Label text="Home Page" class="h2 text-center"></Label>
</GridLayout>
</TabContentItem>
<TabContentItem>
<GridLayout>
<Label text="Account Page" class="h2 text-center"></Label>
</GridLayout>
</TabContentItem>
<TabContentItem>
<GridLayout>
<Label text="Search Page" class="h2 text-center"></Label>
</GridLayout>
</TabContentItem>
I think pages are loaded like a dynamic component. I want to have tabs only with route links. When user tap a tab, I will redirect user to another page.
If I don't use TabContentItem, TabStripItems are also not shown on page. So, I added them with empty contents.
Using selectedIndexChange event I can redirect user to another page, but when one of the tab links is current page, it goes to infinite loop.
It seems like tabs has to be on a different page on this setup. This is not something I want.
Is there a way to convert the BottomNavigation component to a route based one?
Here my current code is:
(It is a Vue project.)
<template>
<BottomNavigation selectedIndex="0" #selectedIndexChange="indexChange">
<TabStrip #itemTap="test">
<template v-for="(tab, key) in tabs">
<TabStripItem :key="key">
<Label :text="tab.title"></Label>
</TabStripItem>
</template>
</TabStrip>
<template v-for="(tab, key) in tabs">
<TabContentItem :key="key">
<GridLayout>
</GridLayout>
</TabContentItem>
</template>
</BottomNavigation>
</template>
<script>
export default {
props: {
tabs: {
type: Array,
required: true
}
},
created () {
},
methods: {
indexChange: function (args) {
let newIndex = args.value
let route = this.tabs[newIndex].route
this.goToPage(route)
},
goToPage (route) {
this.$navigator.navigate(route)
}
}
}
</script>

How to access the item context inside RadListView tkListItemSwipeTemplate

Using NativeScript 3 + Angular 5.
I need to allow the user to swipe an item within a RadListView to reveal a short description about the item.
<RadListView
[items]="featuredVideos"
pullToRefresh="true"
selectionBehavior="None"
(itemSwipeProgressStarted)="onSwipeCellStarted($event)"
swipeActions="true"
(pullToRefreshInitiated)="onPullToRefreshInitiated($event)">
<ng-template tkListItemTemplate let-item="item">
<VideoComponent [video]="item"></VideoComponent>
</ng-template>
<ng-template tkListItemSwipeTemplate let-item="item">
<GridLayout columns="*, 500" class="gridLayoutLayout">
<StackLayout id="short-desc" col="1">
<Label [text]="item.shortDescription" class="body" verticalAlignment="center" horizontalAlignment="center"></Label>
</StackLayout>
</GridLayout>
</ng-template>
</RadListView
I would like to be able to access the current item inside the tkListItemSwipeTemplate so that I can bind the text property of the label to the short description. Currently I am getting the following error
JS: ERROR TypeError: Cannot read property 'shortDescription' of undefined
I know this question is a little old now, but for anyone who comes here looking for an answer, I have managed to work-around this problem. Bind your label text to a different value i.e.:
<Label [text]="myLabelText"...
Then in your onSwipeCellStarted callback, you can use the index property on the ListViewEventData argument and set 'myLabelText' appropriately e.g.:
onSwipeCellStarted(args: ListViewEventData) {
...
this.myLabelText = featuredVideos[args.index].shortDescription;
}
This should get you out of trouble. Hope it helps :)

NativeScript/Angular - Reactive form not working when using actionbar

First I implemented the code below.
app.component.html
<StackLayout [formGroup]="form">
<label text="Name"></label>
<TextField formControlName="Name"></TextField>
<label text="Last Name"></label>
<TextField formControlName="LastName"></TextField>
<button text="save" (tap)="save()"></button>
</StackLayout>
app.component.ts
public form: FormGroup;
constructor(private fb: FormBuilder){
this.form = this.fb.group({
"Name": ["", [Validators.required]],
"LastName": ["", [Validators.required]]
});
}
public save(){
console.log(JSON.stringify(this.form.value));
}
When I run the code above, it's everything alright. I get name and lastname correctly.
The problem occurs when I try to add an action bar to this code.
app.component.html
<ActionBar title="New" class="action-bar">
<ActionItem text="save" (tap)="save()"></ActionItem>
</ActionBar>
<StackLayout [formGroup]="form">
<label text="Name"></label>
<TextField formControlName="Name"></TextField>
<label text="Last Name"></label>
<TextField formControlName="LastName"></TextField>
<button text="save" (tap)="save()"></button>
</StackLayout>
The app.component.ts remains the same.
When I run this code and tap the button inside stacklayout I get name and lastname correctly but when I tap the ActionItem I get an empty string for both name and lastname. Am I missing something?
Your code looks just fine - in fact I have re=-tested it with this test application and everything works as expected on my side.
Side note: If testing on iOS keep in mind that subsequent console logs can be printed only once if they are identical. (this) so it might be that it appears that the log is not printed when in fact the action is done.

Dynamic GridLayout in nativescript with angular 2

I'm very new to Nativescript and Angular and it's my first stab at mobile app development, so please bear with me.
I'm trying to create a basic app with Angular2 and Nativescript. What I'm trying to do, is to display 3 buttons at the bottom of the screen when the app starts (these are created dynamically on initialisation). Once you click any of these buttons, I want this row to be moved up and a second row of buttons to appear. The transition should be animated.
In the first approach I tried I couldn't add the GridLayout to my DockLayout.
The home.html file
<DockLayout #container stretchLastChild="false">
</DockLayout>
the home.component.ts file snippet
export class HomePage implements OnInit {
#ViewChild("container") container: DockLayout;
constructor(private _router: Router, private page: Page) {
var view = new View();
}
ngOnInit() {
this.page.actionBarHidden = true;
this.page.backgroundImage = this.page.ios ? "res://background_large_file.jpg" : "res://background_large_file";
console.dir(this.container);
//Create the grid layout on the page
var bottomNav = new GridLayout();
//Add the GridLayout Columns
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
//Add the GridLayout Row
bottomNav.addRow(new ItemSpec(1, GridUnitType.auto));
//Create the buttons
var FButton = new Button();
FButton.text = "F";
var BButton = new Button();
BButton.text = "B";
var CButton = new Button();
CButton.text= "C";
//Position the buttons
FButton.set('row', '0');
FButton.set('col', '0');
BButton.set('row', '0');
BButton.set('col', '1');
CButton.set('row', '0');
CButton.set('col', '2');
bottomNav.addChild(FButton);
bottomNav.addChild(BButton);
bottomNav.addChild(CButton);
//Attempt to add to DockLayout container
--This is the part I can't get working
}
}
The other approach I was considering would be to hardcode my template and only show the first row. On tap of one of the buttons in the first row I'd move it up and fade in the second row. I couldn't get the reference to the GridLayouts on the page to allow me to do this. I'm thinking I might need to use "let container = this.container;" but am a bit unsure on this.
The home.html file
<DockLayout #container stretchLastChild="false">
<GridLayout #second_row columns="*, *, *" rows="auto" dock="bottom" class="choices hidden">
<Button text="M" row="1" col="0"></Button>
<Button text="F" row="1" col="1"></Button>
<Button text="D" row="1" col="2"></Button>
</GridLayout>
<GridLayout #initial_row columns="*, *, *" rows="auto" dock="bottom" class="choices initial">
<Button text="F" row="0" col="0" (tap)="showF()"></Button>
<Button text="B" row="0" col="1" (tap)="showB()"></Button>
<Button text="C" row="0" col="2" (tap)="showC()"></Button>
</GridLayout>
</DockLayout>
The home.component.ts file
export class HomePage implements OnInit {
#ViewChild("container") container: DockLayout;
constructor(private _router: Router, private page: Page) {
var view = new View();
}
ngOnInit() {
this.page.actionBarHidden = true;
}
showC() {
let container = <View>this.container;
container.animate({
opacity: 1,
duration:200
});
console.log('in here');
}
}
The final layout I'm aiming for is (any taps on the new row of buttons - #second_row - will navigate off to other pages once I set up on tap events and routing):
<DockLayout #container stretchLastChild="false">
<GridLayout #second_row columns="*, *, *" rows="auto" dock="bottom" class="choices hidden">
<Button text="M" row="1" col="0"></Button>
<Button text="F" row="1" col="1"></Button>
<Button text="D" row="1" col="2"></Button>
</GridLayout>
<GridLayout #initial_row columns="*, *, *" rows="auto" dock="bottom" class="choices initial">
<Button text="F" row="0" col="0" (tap)="showF()"></Button>
<Button text="B" row="0" col="1" (tap)="showB()"></Button>
<Button text="C" row="0" col="2" (tap)="showC()"></Button>
</GridLayout>
</DockLayout>
I would appreciate any help and advice on the subject.
Thanks in advance!
I would suggested you to use NativeScript Angular translate animation. With its help you could move up the first GridLayout and to show the next one. You could review my sample project here .In regard to that you could also review below attached articles.
http://docs.nativescript.org/angular/ui/animation.html
http://docs.nativescript.org/angular/tutorial/ng-chapter-4
http://docs.nativescript.org/angular/tutorial/ng-chapter-0
For your first example to add your new gridlayout to your page:
page.content = bottomNav;
Would do the trick.

Resources