I have a list, and I want to get the value of the list item.
The view is as follows
<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 #myFoo id="grocery-list" [text]='"Value is: " + i'></Label>
</StackLayout>
</template>
In typescript I have the following
import { Component,ViewChild,ElementRef } from "#angular/core";
import {topmost} from "ui/frame";
import {ListView} from "ui/list-view";
export class AppComponent {
#ViewChild("myFoo") myFooRef: ElementRef;
public myItems = [];
constructor() {
this.myItems.push("1");
this.myItems.push("2");
this.myItems.push("3");
}
onItemTap(event){
}
}
I can do the following to get the value
onItemTap(event){
let itemValue = this.myItems[event.index];
console.log(itemValue);
}
This will get the value in the array. But this will return the value in the array only.
As you can see in the view I have the string Value is appended to the value.
So how can I access the text property of the label which is tapped on.
You can access the view of your item template via args.view. From that point, I assume that you will have different text in your list-items so it is important to create unique IDs for each Label via binding(using the Angular index). So you can do the following:
<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 [id]="'lbl' + i" [text]='"Value is: " + i'></Label>
</StackLayout>
</template>
</ListView>
and then in your onItemTap
public onItemTap(args: ItemEventData) {
console.log("Item Tapped at cell index: " + args.index);
console.log(args.object); // prints something like ListView(137)
console.log(args.view); // prints something like StackLayout(265)
var lbl = <Label>args.view.getViewById("lbl" + args.index);
console.log(lbl.text); // prints the actual text of the tapped label
}
Related
I am trying to make funcionality, to make larger text across whole application for user when he clicks a 'increase font size' button. Using MVVM, I have done it like this:
Increase font size button click
increase value of double field 'fontSize' which is binded to almost every text in layout
Update UI with new value after button click
However I don't know how to achieve this in Collectionview where I have got Binding in .xaml file, with some particular List (item is model class). The collectionview DataTemplate contains labels where I want to increase font size. Is there a way to do this without adding 'fontSize' field in my model class. If not how to update UI with 'new' List with increased font sizes.
I appreciate any help, tips and discussions.
Thank you.
You can create bindableproperty(fontsize) in your viewmodel and use Relative Binding so the label in Collectionview can change it's fontsize,code like:
ViewMode:
public class ColViewModel:BindableObject
{
public ObservableCollection<Student> students { set; get; }
public static readonly BindableProperty FontSizeProperty =
BindableProperty.Create("fontsize", typeof(int), typeof(ColViewModel), null);
public int fontsize
{
get { return (int)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public ICommand IncreaseCommand { private set; get; }
public ColViewModel()
{students = new ObservableCollection<Student>();
getStudents();
fontsize = 24;
IncreaseCommand = new Command(() => {
fontsize++;
});
}
View:
<StackLayout>
<Label Text="This is a Title" FontSize="{Binding fontsize}"/>
<CollectionView ItemsSource="{Binding students}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" FontSize="{Binding Source={RelativeSource AncestorType={x:Type local:ColViewModel}}, Path=fontsize}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Click to increase fontsize" Command="{Binding IncreaseCommand}"/>
</StackLayout>
Edit:
xmlns:local="clr-namespace:MyForms2.ViewModels"
I've read about messaging, triggers, behaviors, etc. ... all seems a bit overkill for what I am trying to accomplish.
I have a repeating data entry screen in xaml that has 1 picker, 2 entries, and 1 button. The picker, once a value is selected, keeps that selection. The 1st entry does the same as the picker. The 2nd entry is the one that is always getting new values.
I want to collect the filled in values on click of the button and then clear the last entry field of its data and put focus back on that entry so the user can enter a new value and hit save. repeat repeat repeat etc.
I understand the MVVM model and theory - but I just want to put the focus on an entry field in the xaml view and am completely stumped.
EDIT to add code samples
view.xaml:
<StackLayout Spacing="5"
Padding="10,10,10,0">
<Picker x:Name="Direction"
Title="Select Direction"
ItemsSource="{Binding Directions}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding SelectedDirection}"/>
<Label Text="Order"/>
<Entry Text="{Binding Order}"
x:Name="Order" />
<Label Text="Rack"/>
<Entry Text="{Binding Rack}"
x:Name="Rack" />
<Button Text="Save"
Style="{StaticResource Button_Primary}"
Command="{Binding SaveCommand}"
CommandParameter="x:Reference Rack" />
<Label Text="{Binding Summary}"/>
</StackLayout>
viewmodel.cs
public ICommand SaveCommand => new DelegateCommand<View>(PerformSave);
private async void PerformSave(View view)
{
var scan = new Scan()
{
ScanType = "Rack",
Direction = SelectedDirection.Name,
AreaId = 0,
InsertDateTime = DateTime.Now,
ReasonId = 0,
ScanItem = Rack,
OrderNumber = Order,
ScanQty = SelectedDirection.Value,
IsUploaded = false
};
var retVal = _scanService.Insert(scan);
if (!retVal)
{
await _pageDialogService.DisplayAlertAsync("Error", "Something went wrong.", "OK");
}
else
{
view?.Focus();
Rack = string.Empty;
Summary = "last scan was great";
}
}
Error shows up in this section:
private void InitializeComponent() {
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(RackPage));
Direction = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Picker>(this, "Direction");
Order = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Entry>(this, "Order");
Rack = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Entry>(this, "Rack");
}
You can send the Entry as a View parameter to your view model's command. Like that:
public YourViewModel()
{
ButtonCommand = new Command<View>((view) =>
{
// ... Your button clicked stuff ...
view?.Focus();
});
}
And from XAML you call this way:
<Entry x:Name="SecondEntry"
... Entry properties ...
/>
<Button Text="Click Me"
Command="{Binding ButtonCommand}"
CommandParameter="{Reference SecondEntry}"/>
I hope it helps.
You can set the CommandParameter in the XAML and use that in the viewmodel.
In Xaml:
<ContentView Grid.Row="0" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Image Source="downarrow" HeightRequest="15" WidthRequest="15" VerticalOptions="Center" HorizontalOptions="End" Margin="0,0,5,0" />
<ContentView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SecurityPopupCommand}" CommandParameter="{x:Reference AnswerEntry}"/>
</ContentView.GestureRecognizers>
</ContentView>
In view model:
public ICommand SecurityPopupCommand { get { return new Command(OpenSecurityQuestionPopup); } }
private void OpenSecurityQuestionPopup(object obj)
{
var view = obj as Xamarin.Forms.Entry;
view?.Focus();
}
Not sure you can set the focus of an entry from the XAML, but from the page, you could just use the function Focus of the Entry on the Clicked Event:
saveButton.Clicked += async (s, args) =>
{
//save the data you need here
//clear the entries/picker
yourEntry.Focus();
};
Here is what I tried. Note that I removed parts of the XAML to make the question shorter but I tested it out by setting detailx.text in my cs and when I do it that way it displays XXXX:
<Frame x:Class="Japanese.PhrasesFrame">
<StackLayout x:Name="phrasesFrameStackLayout" >
<Grid x:Name="phraseGrid">
<Grid x:Name="wordGrid" >
<Grid x:Name="detailGrid">
<Label x:Name="detail1" Text="{Binding English}" XAlign="Center" />
<Label x:Name="detailx" XAlign="Center" />
In my CS I have
public PhrasesFrame()
{
InitializeComponent();
correctButton.Clicked += correctButtonClicked;
resetButton.Clicked += resetButtonClicked;
SetBindings();
wordGrid.BindingContext = AS.phrase;
}
In a method I have this:
public partial class PhrasesFrame : Frame
{
Random rand = new Random();
public PhrasesFrame()
{
InitializeComponent();
wordGrid.BindingContext = AS.phrase;
AS.phrase = new PSCViewModel() { English = "abcd" };
this.detailx.Text = "XXXX";
}
My problem is that when I used Binding English then the label never shows the text abcd but I do see the XXXX
Can someone tell me if I am missing something obvious. I tried everything but no success.
Try changing the order, like this:
AS.phrase = new PSCViewModel() { English = "abcd" };
wordGrid.BindingContext = AS.phrase;
Whenever you set the BindingContext at that moment it is applied, as is. If you make changes afterwards, you can, but you need to implement the INotifyPropertyChanged event to fire off a signal to the UI indicating which property has changed. Only then will it be update in the UI.
HI for example below is my view
<ScrollView>
<StackLayout>
<StackLayout *ngFor="let kid of kids" id="kidList">
<StackLayout orientation="horizontal" class="some-class">
<Lable text="{{ kid.fname }} {{ kid.lname }}"></Lable>
<Lable text="{{ kid.age }} years ago"></Lable>
</StackLayout>
</StackLayout>
</StackLayout>
</ScrollView>
I want to append the more data getting from server to 'kidList' when scroll reaches to bottom of screen in {N} aAngular2.
It's very hard to me build the layouts and adding childs in 'js' like below(KidInformation has more data).
let stackLayout = new StackLayout();
stackLayout.addChild('something newly constructed with JS')
Is there a way we can do in My Component just by adding child as view by passing local parameters to view , I mean like in below way
let kidListStacklayout = view.getViewById(this.page, 'kidList');
kidListStacklayout.addChild('views/kid/kid-item.html')
and kid-item.html will look like
<StackLayout orientation="horizontal" class="some-class">
<Lable text="{{ kid.fname }} {{ kid.lname }}"></Lable>
<Lable text="{{ kid.age }} years ago"></Lable>
</StackLayout>
The stock list view supports what you want. Don't use a scrollview and adding more layouts to the screen. This will cause lag and other issues. A listview recycles UI view components to reduce overhead of the layout growing in size. You want to use the loadMore event on the list view. https://docs.nativescript.org/cookbook/ui/list-view
Of course as the comment above ^^^ the free UI suite from telerik provides the RadListView which also supports infinite scrolling with a loadMore event.
Find out how to do it ( using Angular & TypeScript) :
import { ScrollView, ScrollEventData } from "ui/scroll-view";
#Component({...})
class ComponentClass implements OnInit, AfterViewInit {
#ViewChild("scrollid") scrollView: ElementRef;
ngOnInit(){
let scrollv : ScrollView = <ScrollView>this.scrollView.nativeElement;
scrollv.on(ScrollView.scrollEvent, function (args: ScrollEventData) {
if(scrollv.scrollableHeight === args.scrollY){
console.log("load more items here !!! ");
}
});
}
}
The scrollv.scrollableHeight gets updated by itself.
Tested on android emulator only. Must work on both Plateforms.
I am trying to create a custom component in Nativescript. The component works fine with the static data, I want to add some custom properties to that control, but the are not accessible in the code behind. I am trying to create a MCQ like box or a something similar to radio control, so user can only select one option from the given ones.
CustomControl.xml
<StackLayout orientation="vertical" class="form" loaded="loaded">
<Repeater items="{{ items }}">
<Repeater.itemTemplate>
<StackLayout orientation="vertical" tap="itemTapped" id="{{id}}">
<StackLayout orientation="horizontal" verticalAlignment="center">
<Label text="{{text}}" class="form-field" width="88%"/>
<Label text="{{characterCode}}" visibility="{{visible ? 'visible' : 'collapsed'}}" class="icon"/>
</StackLayout>
<StackLayout class="separator"/>
</StackLayout>
</Repeater.itemTemplate>
</Repeater>
</StackLayout>
CustomControl.js
var Observable = require("data/observable").Observable;
var ObservableArray = require("data/observable-array").ObservableArray;
var _component;
var _viewModel = new Observable();
var _selectedId = null;
exports.loaded = function(args){
_component = args.object;
//passing in _component.items as array throws undefined
var items = getInitializedArray(["Some text","Someother text"]);
_viewModel.set("items", items);
_component.bindingContext = _viewModel;
}
exports.itemTapped = function(args){
var id = args.object.id;
if(_selectedId === null){
var item = _viewModel.get("items").getItem(id);
item.visible = true;
_viewModel.get("items").setItem(item, id);
}else{
var item = _viewModel.get("items").getItem(_selectedId);
item.visible = false;
item = _viewModel.get("items").getItem(id);
item.visible = true;
_viewModel.get("items").setItem(item, id);
}
_selectedId = id;
}
function getInitializedArray(data){
var id=0;
var items = data.map((listItem) => {
return {
text: listItem,
characterCode: String.fromCharCode(0xea11),
visible: false,
id: id++
}
});
return new ObservableArray(items);
}
Trying to use it in my page as
<CustomComponents:CustomControl items="{{items}}"/>
But using args.object.items throws undefined property for object.
I have read that I'll have to use dependency-observable and will have to create a plugin. But I am not using any platform specific thing, I am just creating a component with existing ui components and its pretty simple what I want to achieve. Is there a way to bind custom properties? Plugins are too complex for this, How can I achieve it?
After reading the docs and going through various forums and github issues following is what I have found and thanks to Nick lliev' comment.
To give custom properties to your controls you'll have to use the code only technique, I have written a blog http://mobile.folio3.com/creating-custom-controls-in-nativescript/ describing both the techniques showing how to give custom properties to your custom controls.