RadListView better scrollToIndex() behavior - nativescript

RadListView.scrollToIndex() seems to display the selected item at the bottom of the current window always - regardless of whether the item is already visible or not.
Is there a function like RadListView.isVisible()?

I have just tested the given scenario with scrollToIndex() and was unable to reproduce such a behavior. Selected row has been always displayed in the top of the screen. You could review my sample code.
main-page.xml
<Page loaded="onPageLoaded" xmlns:lv="nativescript-telerik-ui-pro/listview" xmlns="http://www.nativescript.org/tns.xsd">
<lv:RadListView id="rdid" loaded="radlistviewlaoded" items="{{ dataItems }}" >
<lv:RadListView.listViewLayout>
<lv:ListViewLinearLayout scrollDirection="Vertical"/>
</lv:RadListView.listViewLayout>
<lv:RadListView.itemTemplate>
<StackLayout orientation="vertical">
<Label fontSize="20" text="{{ itemName }}"/>
<Label fontSize="14" text="{{ itemDescription }}"/>
</StackLayout>
</lv:RadListView.itemTemplate>
</lv:RadListView>
</Page>
main-page.ts
import { EventData } from "data/observable";
import { Page } from "ui/page";
import { HelloWorldModel } from "./main-view-model";
import { ObservableArray } from "data/observable-array";
import { RadListView } from "nativescript-telerik-ui-pro/listview";
import {setTimeout} from "timer"
// Event handler for Page "navigatingTo" event attached in main-page.xml
export function onPageLoaded(args: EventData) {
// Get the event sender
var page = <Page>args.object;
var listview:RadListView = <RadListView> page.getViewById("rdid");
console.log("listview visibility "+listview.visibility);
//listview.scrollToIndex(3);
var array = new ObservableArray();
for(var i=0;i<20;i++){
array.push({itemName:"Name "+i, itemDescription:"desc "+i});
}
setTimeout(function(){
listview.scrollToIndex(3)
}, 2000)
page.bindingContext = {dataItems:array};
}
export function radlistviewlaoded(args:EventData){
console.log("RadListView loaded");
}
About the second question you could use loaded event in case you want to know when the view has been loaded on the screen. In case you would like to know if the RadListview is visible you could use visibility property.

Related

OnBlur event without the ngModel & textfield blinking

Nativescript app: I am creating dinamy TextFields.
1) Probme - When i tap on dinamicly generated text field, the keyboard shows for miliseconds and the disapears. When i tap really fast a few times then the keyboard stays.
2) How to make onChange/onBlur event on dinamicly generated TextField? Like when i update the textField i need to call a method.
Here is the current list:
(blur) Does not work:
<StackLayout col="1" row="0">
<ListView [items]="categoryService.attributes | async">
<template let-item="item" let-i="index">
<GridLayout rows="50 100">
<Label [text]="item.name"></Label>
<TextField #input *ngIf="item.type=='text'" row="1" hint="Enter Value here" [text]="item.name" (blur)="categoryService.onAttributeChange(item, item.type, null, input.value)"></ TextField>
<Switch #switch *ngIf="item.type=='checkbox'" row="1" checked="false" (checkedChange)="categoryService.onAttributeChange(item, item.type, null, switch.checked)"></Switch>
<DropDown #aa
*ngIf="item.type=='select'"
row="1"
[items]="categoryService.showAttributeValues(item.value)"
[selectedIndex]="selectedIndex"
(selectedIndexChange)="categoryService.onAttributeChange(item, item.type, aa.selectedIndex)"></DropDown>
</GridLayout>
</template>
</ListView>
</StackLayout>
Thanks!
About your second question you could use textChange method and to return $event as argument this will help you to get text for every TextField individually. You could review the sample code below. About the problem with showing the keyboard, it could be something related with the listview itemTap event. However this problem has been reproduced only on Android and still looking for possible solution.
app.component.html
<StackLayout>
<ListView [items]="myItems" (itemTap)="onItemTap($event)">
<template let-item="item" let-i="index" let-odd="odd" let-even="even">
<StackLayout [class.odd]="odd" [class.even]="even">
<Label [text]='"index: " + i'></Label>
<Label [text]='"[" + item.id +"] " + item.name'></Label>
<TextField (tap)="onTap($event)" hint="Enter text" text="" (textChange)="ontextChange($event)"></TextField>
</StackLayout>
</template>
</ListView>
</StackLayout>
app.component.ts
import {Component, Input, ChangeDetectionStrategy} from '#angular/core';
import {TextField} from "ui/text-field";
import app = require("application");
class DataItem {
constructor(public id: number, public name: string) { }
}
#Component({
selector: "my-app",
templateUrl: "app.component.html",
})
export class AppComponent {
public myItems: Array<DataItem>;
private counter: number;
public status =true;
constructor() {
this.myItems = [];
this.counter = 0;
for (var i = 0; i < 50; i++) {
this.myItems.push(new DataItem(i, "data item " + i));
this.counter = i;
}
}
public onItemTap(args) {
console.log("------------------------ ItemTapped: " + args.index);
}
public ontextChange(args){
console.log("text "+args.object.text);
}
}
I hope this helps

SegmentationBar change updating model but not updating the UI

I am having an issue with updating the UI when changing the index of a SegmentedBar component.
I started with trying to use the Segmented bar with angular/nativescript Router navigation. I could get the SegmentedBar to update the index of the bar and then fire a navigation change but the view didn't update any of the UI with dynamic data from the component.
So I simplified it a bit to have all of the UI rendered in the app.component, including the SegmentedBar. But again the same issue, I am checking for a change on the SegmentedBar and then updating the variable but its doesn't reflect the changes in the UI.
Is this a bug with the component or am I doing something wrong?
import {Component, AfterViewInit, OnInit, ViewChild, ElementRef} from "#angular/core";
import {TabsComponent} from "./components/tabs/tabs.component";
import {HTTP_PROVIDERS, Http} from '#angular/http';
import {ROUTER_DIRECTIVES, Router} from '#angular/router';
import {NS_ROUTER_DIRECTIVES} from 'nativescript-angular/router';
import {SegmentedBar, SegmentedBarItem, SelectedIndexChangedEventData} from 'ui/segmented-bar';
#Component({
selector: 'my-app',
//directives: [ROUTER_DIRECTIVES, NS_ROUTER_DIRECTIVES, TabsComponent],
//providers: [HTTP_PROVIDERS, TodoService],
template: `
<ActionBar title="Calculator" class="ui-action-bar">
<ActionItem tap="onShare"
ios.systemIcon="9" ios.position="right"
android.systemIcon="ic_menu_share" android.position="actionBar"></ActionItem>
</ActionBar>
<StackLayout>
<StackLayout class="ui-nav">
<SegmentedBar #tabs [items]="items" [selectedIndex]="selectedIndex"></SegmentedBar>
</StackLayout>
<StackLayout class="o-section o-section--edge-padding" orientation="vertical" visibility="{{ selectedIndex == 1 ? 'visible' : 'collapse' }}">
<Label text="Home" class="ui-dia-section__title"></Label>
<Label [text]="selectedIndex" class="ui-dia-section__title"></Label>
<Label text="{{selectedIndex}}" class="ui-dia-section__title"></Label>
</StackLayout>
<StackLayout class="o-section o-section--edge-padding" orientation="vertical" visibility="{{ selectedIndex == 0 ? 'visible' : 'collapse' }}">
<Label text="Glossary" class="ui-dia-section__title"></Label>
<Label [text]="selectedIndex" class="ui-dia-section__title"></Label>
</StackLayout>
</StackLayout>
`})
export class AppComponent {
selectedIndex: number;
items: Array<any>;
showHomeScreen: boolean = true;
showGlossaryScreen: boolean = false;
#ViewChild("tabs") tabs: ElementRef;
constructor() {
this.selectedIndex = 0;
this.items = [{ title: 'Calculator' }, { title: 'Glossary' }];
}
ngAfterViewInit() {
this.tabs.nativeElement.on(SegmentedBar.selectedIndexChangedEvent, (args: SelectedIndexChangedEventData) => {
switch (args.newIndex) {
case 0:
console.log('first selected, selectedIndex: ' + this.selectedIndex);
//this.router.navigateByUrl("home");
this.selectedIndex = 0;
break;
case 1:
console.log('second selected, selectedIndex: ' + this.selectedIndex);
// this.router.navigateByUrl("glossary");
this.selectedIndex = 1;
break;
case 3:
console.log('third selected')
break;
}
})
}
ngOnInit(){
console.log('ngOnInit, index: ' + this.selectedIndex);
}
}
hard to read the code but it seems to be a two way binding -banana in the box- issue ?
try: [(ngModel)]="selectedIndex"
instead of: text="{{selectedIndex}}"

in Listview while scrolling and fast click on item unable to stop scroll, intead it will open a item?

In listview inside i have listview template and its inside have different element and different tap event listener.
In IOS scroll fast and fast touch on screen.
it navigates to item page instead of stop scrolling so i would like to approach below method to stop scroll. Unable to fix.
Expected Result: To Stop Scroll
If touch on screen to stop scroll and then i touch on screen open item page.
Actual Result: It goes to els part, Unable to stop scroll.
While scrolling touch on screen(Tap event raised) instead of stop the scroll.
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo">
<ListView items="{{ menu }}" row="1" id="mylist" >
<ListView.itemTemplate>
<GridLayout columns="auto, *">
<Image src="{{ imageURL }}" row="0" cssClass="icon" tap="myTapListener"/>
<StackLayout col="1" tap="myTapListener1">
<Label text="{{ title }}" cssClass="name"/>
<Label text="{{ subtitle }}" cssClass="location"/>
</StackLayout>
</GridLayout>
</ListView.itemTemplate>
</ListView>
</Page
exports.myTapListener = function(args) {
var item = args.object;
if(item.page.getViewById("mylist").isScrolling) {
console.log("Scroll stopped: " + item.page.getViewById("mylist).isScrolling);
return;
} else {
console.log("unable to stop:" + item.page.getViewById("mylist).isScrolling);
}
}
exports.myTapListener1 = function(args) {
var item = args.object;
if(item.page.getViewById("mylist").isScrolling) {
console.log("Scroll stopped: " + item.page.getViewById("mylist).isScrolling);
return;
} else {
console.log("unable to stop:" + item.page.getViewById("mylist).isScrolling);
}
}
The Js code needs to be:
var frame = require('ui/frame');
exports.myTapListener = function(args) {
var page = frame.topmost().currentPage;
if(page.getViewById("mylist").isScrolling) {
console.log(page.getViewById("mylist").isScrolling);
} else {
topmost().navigate("module/a1")
}
}
exports.myTapListener1 = function(args) {
var page = frame.topmost().currentPage;
if(page.getViewById("mylist").isScrolling) {
console.log(page.getViewById("mylist).isScrolling);
} else {
topmost().navigate("module/a2")
}
}
args.object in these event handlers is NOT the page object; but the item generating the event. So you just need to reference to the currentPage which frame.topmost().currentPage

TextChange in repeater nativescript

i trying do to textchange in repeater and research this link.
Basic blur event in a Telerik Nativescript Mobile App
It work in single textfield but no work in repeater. Isn't set wrong anything?
XML:
<Repeater id="lstSelectedItemsSingle" items="{{itemsSingle}}">
<Repeater.itemTemplate>
<GridLayout columns="auto,*,auto,*,auto" rows="auto,auto,1" padding="6" id = "{{ matchId + dataType + 'GridSingle'}}">
<GridLayout columns="*,*,*" rows="40" col="3" borderRadius="6" borderWidth="1" borderColor="#DBDBDB" >
<button backgroundImage="res://reduce_enable" style="background-repeat:no-repeat;background-position: 50% 50%" backgroundColor="#BFBFBF" />
<TextField col="1" backgroundColor="#ffffff" col="1" text="{{stake}}" style="text-align:center" keyboardType="number" />
<button backgroundImage="res://add_icon_enable" col="2" style="background-repeat:no-repeat;background-position: 50% 50%" backgroundColor="#BFBFBF" col="2"/>
</GridLayout>
</GridLayout>
</Repeater.itemTemplate>
</Repeater>
Model:
exports.onPageLoaded = function(args){
page = args.object;
viewM.set("stake", "2");
viewM.addEventListener(observable.Observable.propertyChangeEvent, function (event) {
console.log(event.propertyName);
}
});
}
It's probably because repeaters are bound to a list of items - usually observables. If you bind inside the repeater using "{{ }}", NativeScript is going to look for that method on that specific object in the repeater. So your code should be structured something like this (TypeScript) ...
import { Observable, EventData } from 'data/observable';
import { Page } from 'ui/page';
class Item extends Observable({
text: string = '';
constructor(text: string) {
this.text = text;
this.todos.on(ObservableArray.changeEvent, (args: any) => {
// handle text change
});
}
});
class ViewModel extends Observable({
items: ObservableArray<Items>
constructor() {
this.items = new ObservableArray<Items>({
new Item('Thing 1'),
new Item('Thing 2')
});
}
});
let loaded = (args: EventData) => {
let page = <Page>args.object;
page.bindingContext = new ViewModel();
}
export { loaded }

How to add delete button and making it work in nativescript

I'm trying to add a delete button and making it work, i'm splitting up the problems in two.
In my xml file I have this:
<Page loaded="onPageLoaded">
<GridLayout rows="auto, *">
<StackLayout orientation="horizontal" row="0">
<TextField width="200" text="{{ task }}" hint="Enter a task" id="task" />
<Button cssClass="test" text="Add" tap="add"></Button>
<Button cssClass="test" text="Refresh" tap="refresh"></Button>
</StackLayout>
<ListView items="{{ tasks }}" row="1">
<ListView.itemTemplate>
<Label text="{{ name }}" />
<Button cssClass="test" text="X" tap="delbutton"></Button>
</ListView.itemTemplate>
</ListView>
</GridLayout>
</Page>
The first problem is the delbutton, which is the delete button, if i add it like that it will replace my view with a bunch of X's. I cant seem to understand why.
The second problem i'm having trouble with is how to make it work so that it loops through and deletes the item i want to delete, what im cyrrently doing is getting data form a backend server with json that looks like this:
exports.onPageLoaded = function(args) {
page = args.object;
pageData.set("task", "");
pageData.set("tasks", tasks);
page.bindingContext = pageData;
var result;
http.request({
url: "http://192.168.1.68:3000/posts.json",
method: "GET",
headers: { "Content-Type": "application/json" },
}).then(function (response) {
result = response.content.toJSON();
for (var i in result) {
tasks.push({ name: result[i].name });
}
}, function (e) {
console.log("Error occurred " + e);
});
};
exports.delbutton = function() {
console.log("REM")
};
Thanks for your help and time.
The first problem (that only the X is showing) is due to the fact that a ListView item wants exactly one (1) child. You have two (a Label and a Button). Fortunately one item might be a so what you want to do is to enclose your two elements in a StackLayout, like this:
<Page loaded="onPageLoaded">
<GridLayout rows="auto, *">
<StackLayout orientation="horizontal" row="0">
<TextField width="200" text="{{ task }}" hint="Enter a task" id="task" />
<Button cssClass="test" text="Add" tap="add"></Button>
<Button cssClass="test" text="Refresh" tap="refresh"></Button>
</StackLayout>
<ListView items="{{ tasks }}" row="1">
<ListView.itemTemplate>
<StackLayout orientation="horizontal">
<Label text="{{ name }}" />
<Label text="{{ hello }}" />
<Button cssClass="test" text="X" tap="delbutton"></Button>
</StackLayout>
</ListView.itemTemplate>
</ListView>
</GridLayout>
</Page>
As for the second part of removing items from the ListView. I don't know if your pageData is an observable object as the declaration is not part of your pasted code, but I'm guessing it is. Anyways, I've created an example of how to populate data using observables (which is the NativeScript way of building ui:s, see previous link) and how to remove an item from the ListView.
I've added comments in the code to explain what I'm doing.
var Observable = require('data/observable');
var ObservableArray = require('data/observable-array');
/**
* Creating an observable object,
* see documentation: https://docs.nativescript.org/bindings.html
*
* Populate that observable object with an (empty) observable array.
* This way we can modify the array (e.g. remove an item) and
* the UI will reflect those changes (and remove if from the ui
* as well).
*
* Observable objects are one of NativeScripts most fundamental parts
* for building user interfaces as they will allow us to
* change an object and that change gets propagated to the ui
* without us doing anything.
*
*/
var contextArr = new ObservableArray.ObservableArray();
var contextObj = new Observable.Observable({
tasks: contextArr
});
exports.onPageLoaded = function(args) {
var page = args.object;
page.bindingContext = contextObj;
/**
* Simulating adding data to array after http request has returned json.
* Also adding an ID to each item so that we can refer to that when we're
* removing it.
*/
contextArr.push({name: 'First Item', id: contextArr.length});
contextArr.push({name: 'Second Item', id: contextArr.length});
contextArr.push({name: 'Third Item', id: contextArr.length});
};
exports.delbutton = function(args) {
/**
* Getting the "bindingContext" of the tapped item.
* The bindingContext will contain e.g: {name: 'First Item', id: 0}
*/
var btn = args.object;
var tappedItemData = btn.bindingContext;
/**
* Iterate through our array and if the tapped item id
* is the same as the id of the id of the current iteration
* then remove it.
*/
contextArr.some(function (item, index) {
if(item.id === tappedItemData.id) {
contextArr.splice(index, 1);
return false;
}
});
};

Resources