I would like to know if it's possible to have UIScrollView with fade effect with nativescript please ?
For example : https://medium.com/#luisfmachado/uiscrollview-with-fade-effect-246e332e8b24
I read the documentation https://nativescript-vue.org/en/docs/elements/components/scroll-view/, but I don't found this information.
I would like this result for example :
Do you have an idea please ? Thank you
I have no idea how can I put the native code in my component
<template>
<ScrollView class="scroll" orientation="vertical" row="1" ref="scrollView">
<StackLayout marginLeft="10" marginRight="10" class="container-verses">
<StackLayout horizontalAlignment="center">
<Label textWrap="true" textAlignment="center" text="hello" color="#FFFFFF" fontSize="20"/>
...
<Label textWrap="true" textAlignment="center" text="hello" color="#FFFFFF" fontSize="20"/>
</StackLayout>
</StackLayout>
</ScrollView>
</template>
<script>
export default {
name : 'FadeScrollView',
computed: {},
methods : {
//
}
};
</script>
<style lang='scss' scoped>
</style>
Here is how you translate Swift into NativeScript
import { isIOS } from "#nativescript/core/platform";
import { ScrollView } from "#nativescript/core/ui/scroll-view";
let FadeScrollViewImpl;
if (isIOS) {
FadeScrollViewImpl = UIScrollView.extend({
fadePercentage: 0.2,
gradientLayer: CAGradientLayer.new(),
transparentColor: UIColor.clearColor.CGColor,
opaqueColor: UIColor.blackColor.CGColor,
topOpacity: () => {
const scrollViewHeight = this.frame.size.height;
const scrollContentSizeHeight = this.contentSize.height;
const scrollOffset = this.contentOffset.y;
const alpha = (scrollViewHeight >= scrollContentSizeHeight || scrollOffset <= 0) ? 1 : 0;
return UIColor.alloc().initWithWhiteAlpha(0, alpha).CGColor;
},
bottomOpacity: () => {
const scrollViewHeight = this.frame.size.height;
const scrollContentSizeHeight = this.contentSize.height;
const scrollOffset = this.contentOffset.y;
const alpha = (scrollViewHeight >= scrollContentSizeHeight || scrollOffset + scrollViewHeight >= scrollContentSizeHeight) ? 1 : 0
return UIColor.alloc().initWithWhiteAlpha(0, alpha).CGColor;
},
layoutSubviews() {
super.layoutSubviews()
this.delegate = this;
const maskLayer = CALayer.new();
maskLayer.frame = this.bounds;
this.gradientLayer.frame = CGRectMake(this.bounds.origin.x, 0, this.bounds.size.width, this.bounds.size.height);
this.gradientLayer.colors = [this.topOpacity, this.opaqueColor, this.opaqueColor, this.bottomOpacity];
this.gradientLayer.locations = [0, NSNumber.alloc().initWithFloat(this.fadePercentage), NSNumber.alloc().initWithFloat(1 - this.fadePercentage), 1];
maskLayer.addSublayer(this.gradientLayer);
this.layer.mask = maskLayer
},
scrollViewDidScroll(scrollView) {
this.gradientLayer.colors = [topOpacity, opaqueColor, opaqueColor, bottomOpacity];
}
});
}
export class FadeScrollView extends ScrollView {
createNativeView() {
if (isIOS) {
return FadeScrollViewImpl.new();
} else {
return super.createNativeView();
}
}
attachNative() {
if (!isIOS) {
super.attachNative();
}
}
}
Then you just have to register the element to start using it in template
Vue.registerElement('FadeScrollView', () => require('./fade-scrollView').FadeScrollView)
Playground Sample
Related
I'm looking for an open source NS7 replacement for the nativescript-drop-down menu. Does anyone have suggestions. I'm porting my app to NS7 and having trouble finding a replacement for what has gone to a paid version of the plugin.
My workaround was to open a modal that has the dropdown items and then receiving the chosen item in the closeCallback function, like so:
dropdown-modal.xml
<Page ios:class="bg-light" xmlns="http://schemas.nativescript.org/tns.xsd" shownModally="onShownModally">
<StackLayout class="modal-view">
<Label android:class="p-l-10 font-weight-normal font-size-larger" ios:class="h2 font-weight-bold p-l-15 p-t-10" text="{{ title }}"></Label>
<Label class="hr"></Label>
<ScrollView class="page" height="40%">
<ListView class="list-group" itemTap="{{ onItemTap }}" items="{{ data }}">
<ListView.itemTemplate>
<Label android:class="h4 text-center font-weight-bold p-b-10" ios:class="h3 text-center font-weight-bold p-y-10" width="100%" text="{{ value }}"
textWrap="true"></Label>
</ListView.itemTemplate>
</ListView>
</ScrollView>
</StackLayout>
</Page>
Notice the difference between Android and iOS classes. This makes the dropdown more system-like.
dropdown-modal.js
import { DropDownViewModel } from "./dropdown-view-model";
export const onShownModally = function(args) {
const dropDownViewModel = new DropDownViewModel(args.context.title, args.context.list, args.closeCallback);
const page = args.object;
page.bindingContext = dropDownViewModel;
};
dropdown-view-model.js
import { Observable, ObservableArray } from "#nativescript/core";
export class DropDownViewModel extends Observable {
constructor(title, items, closeCallback) {
super();
this.title = title;
this.data = new ObservableArray(items);
this.selectedItem = '';
this.closeCallback = closeCallback;
}
onItemTap(args) {
this.selectedItem = args.view.bindingContext;
this.closeCallback(this.selectedItem);
}
}
And this is how it is called in another page.
sample-page.xml
<!--Roles-->
<StackLayout class="m-y-10 m-x-2" row="2" col="1">
<Label class="far h3 p-l-15 text-black m-b-1" text=" Role" textWrap="true" />
<TextField editable="false" tap="{{ toggleDropdown }}" dataListId="roles" dataName="role" dataTitle="Role" class="h4 fal text-black input-border-rounded-lg" text="{{ role.value }}" />
</StackLayout>
Notice how dataListId, dataName, and dataTitle were passed.
sample-page.js
import { SamplePageViewModel } from "./sample-page-view-model";
const samplePageViewModel = new SamplePageViewModel();
export const onNavigatingTo = async function(args) {
await samplePageViewModel.populateRolesList();
};
export const onNavigatedTo = async function(args) {
const page = args.object;
page.bindingContext = samplePageViewModel;
};
sample-page-view-model.js
import { Frame, Observable } from "#nativescript/core";
import { LookupService } from "~/services/lookupService";
import { AppSettingsUtility } from "~/utilities/appSettingsUtility";
export class SamplePageViewModel extends Observable {
constructor() {
super();
this.lookupService = new LookupService();
this.appSettingsUtility = new AppSettingsUtility();
this.routes = this.appSettingsUtility.getRoutes();
this.roles = [];
this.role = { code: 0, value: '' };
}
async populateRolesList() {
this.set('roles', await this.lookupService.getRoles()); // assume roles list
}
toggleDropdown(args) {
const page = args.object.page;
const that = this;
const options = {
context: { title: args.object.dataTitle, list: that.get(args.object.dataListId) },
closeCallback: (selectedItem) => {
if(selectedItem)
that.set(args.object.dataName, selectedItem);
}
};
page.showModal(this.routes.dropdown, options);
}
}
I want to do this:
RelativeLayout.YConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=info_box,
Property=Y+Height,
Factor=1,
Constant=22
}
Is there any way of achieving this in xaml or am I too attached to xaml and should not worry about it and do things like this in code?
I'm afraid you have to do it in code behind and here is an example I tried:
public MainPage()
{
InitializeComponent();
RelativeLayout layout = new RelativeLayout();
BoxView redBox = new BoxView() {BackgroundColor = Color.Red };
BoxView blueBox = new BoxView() { BackgroundColor = Color.Blue };
layout.Children.Add(redBox, Constraint.RelativeToParent((parent) => {
return parent.X;
}), Constraint.RelativeToParent((parent) => {
return parent.Y * .15;
}), Constraint.RelativeToParent((parent) => {
return parent.Width;
}), Constraint.RelativeToParent((parent) => {
return parent.Height * .8;
}));
layout.Children.Add(blueBox, Constraint.RelativeToView(redBox, (Parent, sibling) => {
return sibling.X + 20;
}), Constraint.RelativeToView(redBox, (parent, sibling) => {
return sibling.Y + redBox.Height;
}), Constraint.RelativeToParent((parent) => {
return parent.Width * .5;
}), Constraint.RelativeToParent((parent) => {
return parent.Height * .5;
}));
Content = layout;
}
Or define the controls in the xaml and change the YConstraint in code behind:
RelativeLayout.SetYConstraint(blueBox, Constraint.RelativeToView(redBox, (parent, sibling) =>
{
return sibling.Y + sibling.Height;
}));
In xaml:
<RelativeLayout>
<BoxView Color="Red" x:Name="redBox"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
Property=Height,Factor=.15,Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=1,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=.4,Constant=0}" />
<BoxView Color="Blue" x:Name="blueBox"
RelativeLayout.YConstraint="{ ConstraintExpression Type=RelativeToView,ElementName=redBox,Property=Y,Factor= 1, Constant= 0}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,
ElementName=redBox,Property=X,Factor=1,Constant=20}"
RelativeLayout.WidthConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Width,Factor=.5,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression
Type=RelativeToParent,Property=Height,Factor=.5,Constant=0}" />
</RelativeLayout>
BTW, you can easily achieve this layout by using a Grid (with single column) or Vertical StackLayout.
I am new to nativescript and angular2. I want to filter listview using textfield input entered by user. in angular version 1, we used to do it like
<input type="text" ng-model="userinput">
<div ng-repeat="x in items | filter : userinput">
</div>
how can i do this using angular2 in nativescript?
my listview is:
<ListView [items]="myItems" class="list-group">
<template let-item="item">
<StackLayout>
<Label [text]='item.Department' class="list-group-item"></Label>
</StackLayout>
</template>
</ListView>
and in my component:
export class someComponent {
public myItem: Array<any>;
public isLoading: boolean;
public constructor(private http: Http) {
this.myItem = [];
this.isLoading = true;
}
public ngOnInit()
{
this.http.get("some_api_url")
.map(result => result.json())
.do(result => console.log(JSON.stringify(result)))
.subscribe(result => {
this.myItem = result;
this.isLoading = false;
}, error => {
console.log("ERROR: ", error);
});
}
}
You had to create a pipe for filtering first, something like:
#Pipe({
name: 'filter'
})
#Injectable()
export class FilterPipe implements PipeTransform {
transform(items: any[], field : string, value : string): any[] {
if (!items) return [];
return items.filter(it => it[field] == value);
}
}
Usage:
<li *ngFor="let it of its | filter : 'name' : 'value or variable'">{{it}}</li>
The nativescript ui listview filtering is slow when the data is huge and it does "remove" the no match item. I change a bit code to make it fast and only show filtered data. :)
happy coding :)
Page class="page">
<StackLayout orientation="vertical">
<GridLayout rows="auto,auto,*,auto">
<StackLayout class="form" row="0" orientation="horizontal" width="100%">
<TextField hint="Search..." class="input"
id= "searchstr"
[(ngModel)]="searchstr"
width="80%"></TextField>
<Button class="btn-sm btn-primary btn-active"
id="btnSearch"
(tap)="onSearchTap($event)"
width="20%" height="40" >
<FormattedString>
<Span [text]="iconval" class="icon"></Span>
</FormattedString>
</Button>
</StackLayout>
<StackLayout row="1" orientation="horizontal" width="100%" backgroundcolor="black">
<Label text="CODE" width="25%" class="caption"></Label>
<Label text="NAME" width="75%" class="caption"></Label>
</StackLayout>
<ScrollView row="2" tkExampleTitle tkToggleNavButton>
<RadListView [items]="dataItems"
(itemLoading)="onItemLoading($event)">
<ListViewLinearLayout
tkListViewLayout scrollDirection="Vertical"
itemInsertAnimation="Slide"
itemDeleteAnimation="Slide"></ListViewLinearLayout>
<ng-template tkListItemTemplate let-item="item" let-i="index" let-odd="odd" let-even="even">
<StackLayout class="list-item"
(tap)="onItemTap(item)"
orientation="horizontal" width="100%">
<Label [text]="item.custCode" width="25%"></Label>
<Label [text]="item.custName" width="75%"></Label>
</StackLayout>
</ng-template>
</RadListView>
</ScrollView>
</GridLayout>
</StackLayout>
</Page>
import { Component, OnInit } from '#angular/core';
import { ListViewEventData } from 'nativescript-ui-listview';
import { Color } from 'tns-core-modules/color/color';
import { APIService } from '~/app/core/services';
import { Observable } from 'rxjs';
import { CustProfileLight } from '~/app/core/model';
import { ObservableArray } from 'tns-core-modules/data/observable-array/observable-array';
#Component({
selector: 'ns-customer-lookup',
templateUrl: './customer-lookup.component.html',
styleUrls: ['./customer-lookup.component.css'],
moduleId: module.id,
})
export class CustomerLookupComponent implements OnInit {
private _dataItems: ObservableArray<CustProfileLight>;
customer$:Observable<CustProfileLight>
iconval:string;
search:string;
searchstr:string;
items:any;
//private _myFilteringFunc: (item: any) => any;
constructor(private serv:APIService) { }
ngOnInit() {
this.iconval = String.fromCharCode(0xe986);
this.serv.getCustomer().subscribe(resp=>{
this.items = resp;
this._dataItems = new ObservableArray<CustProfileLight>(resp);
})
}
get dataItems(): ObservableArray<CustProfileLight> {
return this._dataItems;
}
onItemLoading(args: ListViewEventData){
if (args.index % 2 === 0) {
args.view.backgroundColor = new Color("#b3ecff");
}
}
onItemTap(item){
}
onSearchTap(e){
const key =this.searchstr;
console.log(key);
let data= this.items.filter(item=>item.custCode.includes(key) ||
item.custName.includes(key) ||
item.address1.includes(key) ||
item.address2.includes(key) ||
item.address3.includes(key) ||
item.address4.includes(key) ||
item.city.includes(key) ||
item.state.includes(key) ||
item.postalCode.includes(key) ||
item.tel.includes(key) ||
item.fax.includes(key) ||
item.contactPerson.includes(key)
);
this._dataItems = new ObservableArray<CustProfileLight>(data);
}
// get FilteringFunc(): (item: any) => any {
// return this._myFilteringFunc;
// }
// set FilteringFunc(value: (item: any) => any) {
// this._myFilteringFunc = value;
// }
}
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}}"
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 }