I'm completely new to NativeScript but it looks like a sweet platform. I'm writing a toy app and below is my setup:
//code behind
import {AddExpenseModel} from "./add-expense-view-model";
import {EventData} from "tns-core-modules/data/observable";
import {Page} from "tns-core-modules/ui/page";
let expenseModel = new AddExpenseModel();
export function navigatingTo(args: EventData) {
let page = <Page>args.object;
let textField = page.getViewById("tags");
textField.on("textChange", (ev)=>{expenseModel.onTagsTextFieldChange(ev)});
page.bindingContext = expenseModel;
}
export function submit() {
expenseModel.createNewExpense()
}
//view-model
import {Observable, PropertyChangeData} from "tns-core-modules/data/observable";
import {ObservableArray} from "tns-core-modules/data/observable-array";
let u = require('underscore');
export class AddExpenseModel extends Observable {
...
public parsed_tags;
constructor() {
super();
this.parsed_tags = new ObservableArray([]);
...
}
public onTagsTextFieldChange(ev) {
let that = this;
// empty
u.forEach(u.range(this.parsed_tags.length), function (_) {
that.parsed_tags.pop()
});
let parsed = this.parseTags(ev.value);
u.forEach(parsed, function (el) {
that.parsed_tags.push(el);
});
}
private getParsedTags() {
//unbox
return u.map(this.parsed_tags, (el: string) => el)
}
private parseTags(tag_string) {
let arr = u.map(tag_string.split(','), (tag: string) => tag.trim().toLocaleLowerCase());
arr = u.uniq(arr);
arr = u.filter(arr, u.negate(u.isEmpty))
return arr;
}
}
//view
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<StackLayout class="p-20">
<Label text="Add new expense"/>
<text-field
id="name" text="{{ name }}"
row="0"/>
<text-field id="amount" stext="{{ amount, amount | numberConverter }}" />
<text-field id="tags" secure="false" text="{{ tags }}"
/>
<WrapLayout orientation="horizontal" height="300" width="300">
<ListView items="{{ parsed_tags }}">
<ListView.itemsLayout>
<Label text="{{ $value }}" width="70" backgroundColor="red"/>
</ListView.itemsLayout>
</ListView>
</WrapLayout>
<button text="Add expense" id="submit-button" tap="submit"/>
</StackLayout>
</Page>
What I want to achieve is that when a user writes in the tags textfield, stylised Labels appear in the WrapLayout. This works, however, the Labels appear always stacked vertically. Here's a screenshot
I tried to move the whole WrapLayout section out of the StackedLayout section, but I get a cannot read property 'on' of undefined, undefined.
Putting Labels inside of WrapLayout not within a ListView (i.e. static) works as expected, which makes me thing I probably misuse either the ListView or the WrapLayout. Any directions will be appreciated :)
Cheers
Update
Following Eddy's advice I used a Repeater. Using the following xml
<Repeater items="{{ parsed_tags }}">
<Repeater.itemsLayout>
<WrapLayout orientation="horizontal" backgroundColor="#d3d3d3"/>
</Repeater.itemsLayout>
<Repeater.itemTemplate>
<Label text="{{ $value }}" backgroundColor="#84ddff" marginRight="5" marginLeft="5"/>
</Repeater.itemTemplate>
</Repeater>
correctly produces this:
I still don't see why using a ListView wouldn't work. I used the debugger from Sidekick and this is what I see.
<ListView items="{{ parsed_tags }}">
<ListView.itemsLayout>
<WrapLayout orientation="horizontal" backgroundColor="#d3d3d3" marginRight="5" marginLeft="5"/>
</ListView.itemsLayout>
<ListView.itemTemplate>
<Label text="{{ $value }}" backgroundColor="#84ddff" marginRight="5" marginLeft="5"/>
</ListView.itemTemplate>
</ListView>
Related
i have a listview to show data from hardcoded array list and i need to make user able to click on any item to show the details of this item in another page , how can i do that ? i tried to create another array for details and make bindingContext and its working good but no data show when converting to details page as you can see here
thats my code :
main-view-model.js:
var Observable = require("data/observable").Observable;
function RegisterViewModel() {
var viewModel = new Observable();
viewModel.shows = [
{name:"Reg1"},
{name:"Reg2"},
{name:"Reg3"},
{name:"Reg4"},
{name:"Reg5"},
];
return viewModel;
}
exports.RegisterViewModel = RegisterViewModel;
main-page.js:
var RegisterViewModel = require("./main-view-model").RegisterViewModel;
var frameModule = require('ui/frame');
var viewModel = new RegisterViewModel();
function RegisterViewModel(args) {
var page = args.object;
page.bindingContext = RegisterViewModel();
}
exports.getInfo = function (args) {
var navigationEntry = {
moduleName: "RegisterDetails",
context: {info:args.view.bindingContext}
}
frameModule.topmost().navigate(navigationEntry);
}
exports.loaded = function(args){
args.object.bindingContext = viewModel;
}
exports.RegisterViewModel = RegisterViewModel;
main-page.xml:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo" class="page" loaded="loaded">
<Page.actionBar>
<ActionBar title="My App" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<StackLayout class="p-20">
<SearchBar id="searchBar" hint="Search" text="" clear="onClear" submit="onSubmit" />
<TabView>
<TabView.items>
<TabViewItem title="register">
<TabViewItem.view>
<ListView items="{{shows}}" tap="getInfo" >
<ListView.itemTemplate>
<Label text="{{name}}" />
</ListView.itemTemplate>
</ListView>
</TabViewItem.view>
</TabViewItem>
<TabViewItem title="Tab 2">
<TabViewItem.view>
<Label text="Label in Tab2" />
</TabViewItem.view>
</TabViewItem>
</TabView.items>
</TabView>
</StackLayout>
</Page>
these for details:
RegisterDetails-model.js
viewModel.shows = [
{name:"Reg01"},
{name:"Reg02"},
{name:"Reg03"},
{name:"Reg04"},
{name:"Reg05"},
];
return gotData;
}
exports.pageLoaded = pageLoaded;
RegisterDetails.js:
var gotData;
function pageLoaded(args) {
var page = args.object;
gotData = page.navigationContext.info;
page.bindingContext={passedData:gotData}
}
exports.pageLoaded = pageLoaded;
RegisterDetails.xml:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo" class="page" loaded="pageLoaded">
<Page.actionBar>
<ActionBar title="Register" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<StackLayout >
<Label text="{{name}}" />
</StackLayout>
</Page>
but when i clicked on any item i go to register details but no data shows in page , and i received this message error in console :
JS: Binding: Property: 'name' is invalid or does not exist. SourceProperty: 'name'
any help?
I am working with Nativescript ListView and have 4 arrays.
//name array
for (var i = 0; i < employeesJson2.length; i++) {
empNameArray.push(employeesJson2[i].Name)
}
// image array
for (var i = 0; i < employeesJson2.length; i++) {
empImageArray.push(employeesJson2[i].Image)
}
// phone array
for (var i = 0; i < employeesJson2.length; i++) {
empPhoneArray.push(employeesJson2[i].Phone)
}
// email array
for (var i = 0; i < employeesJson2.length; i++) {
empEmailArray.push(employeesJson2[i].Email)
}
I need to be able to utilize these 4 arrays in 1 listview row.
Currently I just have
<StackLayout orientation="vertical">
<ListView items="{{ empNameArray }}" id="rolePicker" itemTap="listViewRoleTap" style="text-align: center" rowHeight="50">
<ListView.itemTemplate>
<Label text="{{ $value }}" textWrap="true" style="padding-top: 10; font-size: 19" />
</ListView.itemTemplate>
</ListView>
</StackLayout>
If i add another listview item it doesn't display. Ideally the items would show up in the same row side by side. Am i missing a step? Should I be combining the arrays? If so, How?
Each list view template can only have one root element. That said if you want to visualize, let's say two or three labels, then you will need to wrap then in container layout.
e.g.
<ListView.itemTemplate>
<StackLayout>
<Label text="first label" />
<Label text="{{ $value }}" />
<Label text="third label" />
</StackLayout>
</ListView.itemTemplate>
Another thing - why iterating over one array to create four additional arrays? If your business logic allows then you can simply use the array with your JSON objects to populate your list view.
What $value is providing is an easy way to bind to value that is not a complex object like a string. So if your array items was of the following kind
var myArray = ["ab", "cd", "ef"];
Then you can use $value like in your example to render each of the value of the current item.
e.g.
<ListView items="{{ myArray }}">
<ListView.itemTemplate>
<Label text="{{ $value }}" />
</ListView.itemTemplate>
</ListView>
However, as far as I understand in your case the objects are of the following kind:
var myArrayItem = { "name": "John",
"image": "some-image-path",
"phone": 123456,
"email": "abv#xyz.com" };
So if you want to iterate and visualize your different key-values then you can do it accessing the key in your binding e.g.
<ListView items="{{ employeesJson2 }}">
<ListView.itemTemplate>
<StackLayout>
<Label text="{{ name }}" />
<Image src="{{ image }}" />
<Label text="{{ phone }}" />
<Label text="{{ email }}" />
</StackLayout>
</ListView.itemTemplate>
</ListView>
I'm very new to NativeScript. I chose it compared to React Native because I wanted 100% access to native API. I started playing with native plugins and read some tutorials like this one native libraries
Now I'm trying to follow the same principle with this plugin FloatingActionButton
var app = require("application");
var platformModule = require("platform");
var fs = require("file-system");
var imageSource = require("image-source");
function creatingFab(args) {
var fabMenu = new com.github.clans.fab.FloatingActionMenu(app.android.foregroundActivity);
var fabButton = new com.github.clans.fab.FloatingActionButton(args.object.android);
fabButton.setButtonSize(FloatingActionButton.SIZE_MINI);
fabButton.setLabelText("Hello Button");
//fabButton.setImageResource(imageSource.fromResource("logo"));
fabMenu.addMenuButton(fabButton);
fabMenu.hideMenuButton(false);
}
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo" class="page">
<Page.actionBar>
<ActionBar title="My App" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<StackLayout class="p-20">
<Label text="Tap the button" class="h1 text-center"/>
<Button text="TAP" tap="{{ onTap }}" class="btn btn-primary btn-active"/>
<Label text="{{ message }}" class="h2 text-center" textWrap="true"/>
</StackLayout>
<android>
<placeholder id="fab" tap="fabClick" class="fab-button" margin="15" creatingView="creatingFab"/>
</android>
</Page>
Thank you in advance for your help
try is as this based on this http://docs.nativescript.org/api-reference/classes/application.androidapplication.html#foregroundactivity
var fabMenu = new com.github.clans.fab.FloatingActionMenu(app.AndroidApplication.foregroundActivity);
I have a login button which when clicked will log the user in by making http calls to the server.
While this is happening I want the activity indicator to show up and disable the button and every other thing on the page and just show the activity indicator over it.
Note that I will place time outs and other measures to make sure that the Activity indicator doesn't end up trapping the user.
Also, I do not want the content in the background to disappear. I just want the activity indicator to overlap it.
Here is my ui code which was taken for this SO answer https://stackoverflow.com/a/39124735/4412482
<GridLayout>
<StackLayout>
//page content goes here
</StackLayout>
</StackLayout>
<StackLayout class="dimmer" visibility="{{showLoading ? 'visible' : 'collapsed'}}"></StackLayout>
<GridLayout rows="*" visibility="{{showLoading ? 'visible' : 'collapsed'}}">
<ActivityIndicator busy="true" width="50" height="50" color="#0c60ee"></ActivityIndicator>
</GridLayout>
</GridLayout>
To disable the events to the components you could use isUserInteractionEnabled property, which will disable the whole interaction to the component and then you could show the ActivityIndicator. I am providing sample code below.
app.component.html
<GridLayout rows="*">
<StackLayout row="0" class="p-20">
<Label text="Tap the button" class="h1 text-center"></Label>
<Button text="TAP" (tap)="onTap()" class="btn btn-primary btn-active" [isUserInteractionEnabled]="buttoninteraction" ></Button>
<Label [text]="message" class="h2 text-center" textWrap="true"></Label>
</StackLayout>
<ActivityIndicator row="0" [busy]="acstate" row="1" class="activity-indicator"></ActivityIndicator>
</GridLayout>
app.component.ts
import { Component } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "app.component.html",
})
export class AppComponent {
public counter: number = 16;
public buttoninteraction = true;
public acstate = false;
public get message(): string {
if (this.counter > 0) {
return this.counter + " taps left";
} else {
return "Hoorraaay! \nYou are ready to start building!";
}
}
public onTap() {
this.counter--;
this.buttoninteraction=false;
this.acstate=true;
}
}
Hope this helps.
I want to get a Level text when the listview (i.e,that level) is tapped.I'm able to get the index of that level.But I can not get the level text field.
View:
<Page loaded="loaded">
<GridLayout>
<ListView items="{{ categoryList }}" itemTap="brand">
<ListView.itemTemplate>
<Label text="{{ category }}" horizontalAlignment="left" verticalAlignment="center" />
</ListView.itemTemplate>
</ListView>
</GridLayout>
</Page>
Js Controller:
function getBrand() {
user.register();
}
exports.brand=function (args){
item=args.index;
//what to put here to be able to get the level text property
user.register(item);
}
Edited: You can put the tap listener to the label inside and get the reference through args:
In XML:
<ListView items="{{ categoryList }}">
<ListView.itemTemplate>
<Label text="{{ category }}" tap="brand"/>
</ListView.itemTemplate>
</ListView>
Then in js:
exports.brand = function(args) {
item = args.object;
var text = item.text;
}