I'm trying to have a row of buttons that grow and shrink depending on if other buttons are added or removed. FlexboxLayout seems most appropriate for this job, but I can't seem to figure out how to approach the problem other than manually manipulating the width percentages over a time.
I thought I could set flexGrow on them and then use a class with visibility: collapse to essentially remove buttons. (And then reverse when returned to normal.) This works, but the change is pretty instantaneous. I would like some sort of squishing/stretching animation.
I tried playing with animations, shrinking the scale to 0 like below. Though the button appeared to shrink, it only did toward the center of itself and still held the space it took up (leaving a gap).
I'm playing with a simple example like this:
<FlexboxLayout flexDirection="row">
<Button text="1" width="25%" flexGrow="1"></Button>
<Button text="2" width="25%" id="test-button"></Button>
<Button text="3" width="25%" flexGrow="1"></Button>
<Button text="4" width="25%" flexGrow="1"></Button>
</FlexboxLayout>
And I tried doing something like this, where I want to shrink button #2 to gone:
let testButton = this.page.getViewById("test-button");
testButton.animate({
scale: { x: 0, y: 1},
duration: 500
});
I also tried to do it with keyframes. That seemed to not do anything.
#test-button {
animation-name: shrink;
animation-duration: 2s;
animation-delay: 1s;
}
#keyframes shrink {
from { width: 25%; }
to { width: 0; }
}
I tried to do something like what was mentioned in this answer here for web pages, but that also didn't seem to do anything.
I managed to get it to work by using databinding that manually adjusts the width using setTimeout. But I was wondering if there might be a different route that might be a bit easier to manage? Can't help but wonder if maybe I'm botching something in my other attempts.
You could hide the Button, while using button visibility property and to hide the component, when the animation finished. For your convenience I am attaching sample code
app.component.html
<FlexboxLayout flexDirection="row">
<Button backgroundColor="green" text="1" width="25%" flexGrow="1"></Button>
<Button backgroundColor="blue" (loaded)="buttonLoaded()" text="2" width="25%" id="test-button"></Button>
<Button backgroundColor="green" text="3" width="25%" flexGrow="1"></Button>
<Button backgroundColor="blue" text="4" width="25%" flexGrow="1"></Button>
</FlexboxLayout>
app.component.ts
import { Component } from "#angular/core";
import { Page } from "ui/page"
#Component({
selector: "my-app",
templateUrl: "app.component.html",
})
export class AppComponent {
constructor(private page:Page){}
public buttonLoaded(){
let testButton = this.page.getViewById("test-button");
testButton.animate({
scale: { x: 0, y: 1},
duration: 500
})
.then(()=>{
let testButton = this.page.getViewById("test-button");
testButton.visibility='collapse'
});
}
}
Related
Well going through {N} tutorial I want to achieve this :
But I have trouble showing this delete button.
There is no problem with the image it shows well somewhere else and I also tried putting a Label instead but same result.
Rad Listview component :
<RadListView row="1" [items]="groceryList"
swipeActions="true" (itemSwipeProgressStarted)="onSwipeCellStarted($event)">
<ng-template let-item="item">
<Label [text]="item.name" class="p-15"></Label>
</ng-template>
<GridLayout *tkListItemSwipeTemplate columns="*, auto">
<StackLayout id="delete-view" col="1" (tap)="delete($event)" class="delete-view">
<Image src="~/images/delete.png" ></Image>
</StackLayout>
</GridLayout>
</RadListView>
CSS :
.delete-view {
background-color: #CB1D00;
padding: 20;
}
.delete-view Image {
color: white;
height: 25;
}
TS
onSwipeCellStarted(args: ListViewEventData) {
var swipeLimits = args.data.swipeLimits;
var swipeView = args.object;
var rightItem = swipeView.getViewById<View>("delete-view");
swipeLimits.right = rightItem.getMeasuredWidth();
swipeLimits.left = 0;
swipeLimits.threshold = rightItem.getMeasuredWidth() / 2;
}
delete(args: ListViewEventData) {
let grocery = <Grocery>args.object.bindingContext;
this.groceryService.delete(grocery.id)
.subscribe(() => {
let index = this.groceryList.indexOf(grocery);
this.groceryList.splice(index, 1);
});
}
Deletion feature works well but all what i am getting when swiping is this :
What am I getting wrong here ?
I spent several days earlier this month wrestling with similar issues with RadListView, especially on iOS. It seems to defy logic. I ended up using a negative padding to get to where I could see my labels and icons. If that doesn't help, try removing height: from your css.
I have a radautocomplete in one of my pages and I'm using bottom-navigation in my app.
The first time I navigate to that page is fine, but after that, when I navigate to that page, the suggestions menu automatically pops open as if I had typed something in the autocomplete but I have not. I even put a textfields above that in my form to steal the focus but that didn't make things any better.
Here is a playground sample
In case playground breaks in the future:
App.vue
<template>
<Page actionBarHidden="true">
<BottomNavigation :selectedIndex="activePage">
<TabStrip>
<TabStripItem>
<label text="0" />
</TabStripItem>
<TabStripItem>
<label text="1" />
</TabStripItem>
</TabStrip>
<TabContentItem>
<button text="go to 1" #tap="activePage=1" />
</TabContentItem>
<TabContentItem>
<StackLayout>
<TextField v-model="textFieldValue" hint="Enter text..."
backgroundColor="lightgray" />
<RadAutoCompleteTextView ref="autocomplete"
:items="choices" backgroundColor="lightgray"
completionMode="Contains" returnKeyType="done"
width="100%" borderRadius="5" />
</StackLayout>
</TabContentItem>
</BottomNavigation>
</Page>
</template>
<script>
import {
ObservableArray
} from "tns-core-modules/data/observable-array";
import {
TokenModel
} from "nativescript-ui-autocomplete";
export default {
data() {
return {
textFieldValue: "",
choices: new ObservableArray(
["one", "two", "three"].map(r => new TokenModel(r))
),
activePage: 0
};
}
};
</script>
<style scoped>
TabContentItem>* {
font-size: 30;
text-align: center;
vertical-align: center;
}
</style>
app.js
import Vue from 'nativescript-vue';
import App from './components/App';
import RadAutoComplete from 'nativescript-ui-autocomplete/vue';
Vue.use(RadAutoComplete);
new Vue({ render: h => h('frame', [h(App)]) }).$start();
I guess the issue is specific to Android, iOS seem to work fine. You may raise an issue at Github, meanwhile a possible workaround is to set visibility on suggestion view on unloaded event, toggle it back on textChanged event.
Updated Playground Sample 1
Update
Changing visibility seems to hide the suggestion view but still occupy the same so components below auto complete field becomes inaccessible. I believe setSuggestionViewHeight(...) may solve this.
Updated Playground Sample 2
I have the following hack that draws drop shadows for the various elements that I have:
<template>
<Page>
<TabView>
<TabViewItem title="Tab 1">
<StackLayout #loaded="addShadow">
<Label text="This box does have a shadow." />
</StackLayout>
</TabViewItem>
<TabViewItem title="Tab 2">
<StackLayout>
<Label text="This box doesn't have a shadow." />
</StackLayout>
</TabViewItem>
</TabView>
</Page>
</template>
<script>
export default {
methods: {
addShadow(event) {
if (event.object.ios) {
event.object.ios.layer.masksToBounds = false;
event.object.ios.layer.shadowOpacity = 0.5;
event.object.ios.layer.shadowRadius = 5.0;
event.object.ios.layer.shadowColor = new Color('#000000').ios.CGColor;
event.object.ios.layer.shadowOffset = CGSizeMake(0.0, 2.0);
}
}
}
}
</script>
<style scoped>
StackLayout {
background: #ffffff;
padding: 16;
margin: 16;
}
</style>
However, while the shadow looks perfect, it does not initially appear when the tab is first accessed. I have to first visit the second tab, and only after returning to the first tab, does the drop shadow get drawn.
I assume this is because I need to invoke something to redraw the layer after adding the shadow, but I cannot figure out how to do that. I've tried event.object.ios.layer.setNeedsDisplay() but it has no effect.
How can I ensure the shadow displays on the first tab render?
How to implement RadioButtons in Nativescript + Angular?
I have tried the RadioButton plugin but it has error during compile time.
I have seen a post on stackoverflow stating to use font-awesome but it seems far more difficult to group the radio buttons.
Is it that I am ignoring any other simple method to create UI for something like this :
Label => Question: Select the fruit you like the most?
Radio 1 : Mango
Radio 2 : Apple
Radio 3 : Kiwi
Radio 4 : Banana
Any help in creating such interface?
Using Nativescript 5.0
Try nativescript-checkbox plugin, setting the boxType to circle will give you radio button style. For Grouped Radio Buttons, simply uncheck other radio buttons in group when one is selected.
I am able to achieve it using the Switch property isUserInteractionEnabled.
btw, I use Nativescript-angular.
This will enable interaction with a Switch only if the selected value is different:
<Switch [checked]="selectedValue === 'Mango'" isUserInteractionEnabled="{{ selectedValue !== 'Mango' }}"></Switch>
After looking around and observing several popular ecommerce apps, I found "Dialog box with List" method suitable as common user is comfortable with it.
I have decided to follow this : https://docs.nativescript.org/angular/ui/ng-ui-widgets/dialogs
Meantime, I will be still happy to see pure radio button kind of interface for Nativescript which is good for close group apps having user base with technical background as against retail users.
After trying multiple plugins (all outdated regarding todays angular and nativescript versions). I've decided to try my own solution, which is using the Button nativescript component to emulate the radiobutton behaviour.
The style needs improving, but the basic behaviour is there.
To achieve this, I've created a generic component which I can use anywhere I need it.
Here is its code:
import { Component, Input } from "#angular/core";
import { RadioBtnChoice } from "./radio-btn-form-input.model";
#Component({
selector: "radio-btn-form-input",
templateUrl: "./radio-btn-form-input.component.html",
styleUrls: ["./radio-btn-form-input.component.scss"]
})
export class RadioBtnFormInputComponent {
#Input() choicesRadioBtns: RadioBtnChoice[];
constructor() {}
ngOnInit(): void {}
toggleSelected(choice: RadioBtnChoice): void {
this.choicesRadioBtns.forEach(choice => {
choice.isSelected = !choice.isSelected;
});
console.log(`onTapChangeValue changed value to: ${choice.name}, ${choice.value}, ${choice.isSelected} `);
}
}
.radio-btn {
padding-top: 5px;
padding-right: 20px;
padding-bottom: 5px;
padding-left: 20px;
border-width: 2px;
background-color: #ddd;
margin: 0;
}
.radio-btn.selected {
border-top-color: #555;
border-left-color: #555;
border-right-color: #CCC;
border-bottom-color: #CCC;
background-color: #bbbbbb;
}
<StackLayout orientation="horizontal">
<template *ngFor="let choice of choicesRadioBtns">
<Button text="{{choice.name}}" (tap)="toggleSelected(choice)"
class="radio-btn {{choice.isSelected ? ' selected' : ''}}">
</Button>
</template>
</StackLayout>
This is the model for the radiobuttons inputs:
export interface RadioBtnChoice {
name: string;
value: any;
isSelected: boolean;
}
And this is the form using it:
export class FormUsingRadioButtonsComponent implements OnInit {
public individual: Individual;
public SexType = SexType;
public sexChoicesRadioBtns: RadioBtnChoice[];
constructor() {
this.sexChoicesRadioBtns = [
<RadioBtnChoice> {
name: "male",
value: SexType.M,
isSelected: true
},
<RadioBtnChoice> {
name: "female",
value: SexType.F,
isSelected: false
}
];
}
}
<Scrollview orientation="vertical">
<StackLayout orientation="vertical">
<StackLayout class="input-field">
<Label text="sex" class="input-label"></Label>
<radio-btn-form-input [choicesRadioBtns]="sexChoicesRadioBtns" class="input"></radio-btn-form-input> <------------- Here it is
</StackLayout>
<StackLayout class="input-field">
<TextField class="input" hint="name" keyboardType="" autocorrect="false" autocapitalizationType="none">
</TextField>
</StackLayout>
<StackLayout class="input-field">
<TextField class="input" hint="surname" keyboardType="" autocorrect="false" autocapitalizationType="none">
</TextField>
</StackLayout>
<Button text="Add individual" class="-primary input-field"></Button>
</StackLayout>
</Scrollview>
Is there a way to make a view 'transparent' to any user interactions? For example I have a view (with transparent background) and a button under that view. I want the user could tap the button under that view. If I have a scroller view under that view I want the user interacts with scroller when scroll over that view, so the view doesn't interfere or intercept user's gestures. But only this view should be transparent to user's interactions not its children. So, if I have a button inside that view it behaves normally.
Example XML:
<AbsoluteLayout width="100%" height="100%">
<Button text="Button1" tap="onTap1" />
<GridLayout width="100%" height="100%" backgroundColor="transparent">
<Button text="Button2" tap="onTap2" horizontalAlignment="center" verticalAlignment="center"/>
</GridLayout>
</AbsoluteLayout>
Thank you for your help.
You have multiple approaches to make a view change its color in NativeScript.
For example you can directly change its backgroundColor. Another oiption is to use animation and third option is to use CSS-animation.
Here is a basic example for the first two options.
page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
<StackLayout>
<GridLayout width="300" height="300" id="myGrid" backgroundColor="transparent">
</GridLayout>
<Button text="Tap me" tap="onTap" />
<Button text="Or Tap me" tap="onAnotherTap" />
</StackLayout>
</Page>
page.js
import { EventData } from "data/observable";
import { Page } from "ui/page";
import { HelloWorldModel } from "./main-view-model";
import { GridLayout } from "ui/layouts/grid-layout";
import { Color } from "color";
var myGridView;
export function navigatingTo(args: EventData) {
var page = <Page>args.object;
page.bindingContext = new HelloWorldModel();
// get refference to the view using its id
myGridView = <GridLayout>page.getViewById("myGrid");
}
export function onTap(args:EventData) {
var color = new Color("#FF0000");
myGridView.backgroundColor = color;
}
export function onAnotherTap(args:EventData) {
myGridView.animate({
backgroundColor: new Color("#3D5AFE"),
duration: 3000
});
}
All of the options can be found described in NativeScript documenation