Get the height of a generated view - nativescript

I'm trying to get the height of a StackLayout. This layout is filled with data from my backend, so it's not always the same height. I tried view.getViewById(parent, selector).effectiveHeight, but it keeps outputting 0.
My XML file looks a bit like this:
<Page>
<ActionBar title="..."></ActionBar>
<GridLayout rows="*, auto" columns="*">
<ScrollView row="0" col="0">
<GridLayout rows="*" columns="*">
<StackLayout id="TARGET" row="0" col="1">
<StackLayout *ngFor="let x of y">
<FlexboxLayout justifyContent="space-between">
<FlexboxLayout>
<StackLayout>
...
</StackLayout>
</FlexboxLayout>
<FlexboxLayout justifyContent="space-between">
...
</FlexboxLayout>
</FlexboxLayout>
</StackLayout>
</StackLayout>
<StackLayout col="0" row="0">
...
</StackLayout>
</GridLayout>
</ScrollView>
<StackLayout row="1" col="0">
...
</StackLayout>
</GridLayout>
</Page>
Thank you in advance!

if you are using view.getViewById(parent, selector).effectiveHeight it will only work if view is rendered and loaded. even if you are trying to get values on loaded() event it will be 0 because of nativescript bug.
To workaround this case I would suggest setting up timeout with 200 milliseconds.
for example
home.component.html
<ListView [items]="a" class="list-group">
<ng-template let-country="item" let-i="index" let-odd="odd" let-even="even">
<WrapLayout width="100%" orientation="horizontal" backgroundColor="palegreen">
<StackLayout #abc (loaded)="getSize($event)" width="20%" [height]="testWidth" backgroundColor="lightgray">
<Label [text]="'W'+testWidth" backgroundColor="red"></Label>
<Label text="LA" backgroundColor="green"></Label>
</StackLayout>
</WrapLayout>
</ng-template>
</ListView>
home.component.ts
import { Component, OnInit, ViewChild } from "#angular/core";
import { PercentLength } from "tns-core-modules/ui/styling/style-properties"
import { StackLayout } from "ui/layouts/stack-layout"
#Component({
selector: "Home",
moduleId: module.id,
templateUrl: "./home.component.html",
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
a = ['1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2', '1', '2',]
#ViewChild('abc') abc;
testWidth = 0;
constructor() {
}
getSize(args) {
setTimeout(() => {
let stack= <StackLayout>args.object;
var stackSize = args.object.getActualSize();
var stackWidth = stackSize.width;
var stackHeight = stackSize.height;
console.log("stackWidth: " + stackWidth);
console.log("stackHeight: " + stackHeight);
this.testWidth = stackWidth;
}, 200);
}
ngOnInit(): void {
}
toDevicePixels(percentage: PercentLength) {
console.dir(PercentLength.convertToString(percentage))
return PercentLength.toDevicePixels(percentage,0,0);
}
}
The code sample also shows, how to get the height and width of the StackLayout dynamically and bind them to view.

Use .getMeasuredHeight() once the view is loaded.
Source: https://docs.nativescript.org/api-reference/classes/_ui_core_view_.view#getmeasuredheight

Related

The spacing issue comes when using the side drawer and scroll view in native script angular

I am developing android and ios applications using native script-angular. I am using UISidedrawer plugin for showing the menu's, I have an issue while using UISidedrawer and ScrollView same time, In my component, space was automatically taken. Here are my code and screenshot.
I am creating a side drawer and menu's like:
<RadSideDrawer [transition]="drawerTransition">
<GridLayout rows="auto,*,110" column="auto" tkDrawerContent height="100%">
<StackLayout row="0" column="0" orientation="vertical" class="sidedrawer-header">
<Label class="italic" text="Welcome "></Label>
</StackLayout>
<ListView row="1" column="0" [items] = "navMenu" rowHeight="40" class="sidedrawer-content" separatorColor="#40053e" (itemLoading)="onItemLoading($event)">
<ng-template>
<StackLayout
//code
</StackLayout>
</ng-template>
</ListView>
<StackLayout row="2" column="0" class="sidedrawer-footer" orientation="vertical">
<Button text="Logout" (tap)="logout()" pageTransition="slideright"></Button>
</StackLayout>
</GridLayout>
<StackLayout tkMainContent>
<StackLayout>
<ng-content></ng-content>
</StackLayout>
</StackLayout>
</RadSideDrawer>
My side drawer component.ts code
import { RadSideDrawerComponent, SideDrawerType } from 'nativescript-ui-sidedrawer/angular';
import { SlideInOnTopTransition } from 'nativescript-ui-sidedrawer';
#Component({
moduleId: module.id,
selector: "side-drawer",
templateUrl: 'side-drawer.component.html',
styleUrls: ['side-drawer.component.css']
})
export class SideDrawerComponent implements AfterViewInit, OnDestroy, OnInit {
#ViewChild(RadSideDrawerComponent) drawerComponent: RadSideDrawerComponent;
private drawer: SideDrawerType;
drawerTransition: any;
navMenu: any[] = [
{id: '0', name: 'Dashboard', icon: '~/Images/dashboard.png', commands: ['/dashboard'] },
{id: '1', name: 'Status', icon: '~/Images/progress.png', commands: ['/status'] },
{id: '2', name: 'Transcript', icon: '~/Images/send.png', commands: ['/transcript'] },
{id: '3', name: 'Change Password', icon: '~/Images/password.png', commands: ['/password'] },
{id: '4', name: 'Support', icon: '~/Images/contact.png', commands: ['/contact'] },
{id: '5', name: 'About Us', icon: '~/Images/about.png', commands: ['/about'] },
{id: '6', name: 'FAQ', icon:'~/Images/faq.png',commands:['/faq']},
];
constructor( ) {
this.drawerTransition = new SlideInOnTopTransition();
}
ngAfterViewInit() {
this.drawer = this.drawerComponent.sideDrawer;
}
}
My menu content html
<side-drawer>
<GridLayout rows="*,100">
<StackLayout row="0" orientation="vertical">
<StackLayout orienatation="vertical">
<ScrollView>
<Label text="Hello World> </Label>
</ScrollView>
</StackLayout>
</StackLayout>
<StackLayout row="1">
</StackLayout>
</GridLayout>
</side-drawer>
How to remove unwanted space. Only the space issue occurs when we use a scroll view inside the side drawer and ios platform only.

Why is this route not routing through `page-router-outlet`?

I'm trying to have a component that has an ActionBar and child components that are accessed by page-router-outlet.
My HTML for the parent component is as follows:
<ActionBar class="action-bar">
<NavigationButton ios:visibility="collapsed" icon="res://menu" (tap)="onDrawerButtonTap()"></NavigationButton>
<ActionItem icon="res://navigation/menu" android:visibility="collapsed" (tap)="onDrawerButtonTap()"
ios.position="left">
</ActionItem>
<Label class="action-bar-title" text="Example Text"></Label>
</ActionBar>
<RadSideDrawer #drawer showOverNavigation="true" [drawerTransition]="sideDrawerTransition">
<StackLayout tkDrawerContent>
<MyDrawer [selectedPage]="'Settings'"></MyDrawer>
</StackLayout>
<StackLayout class="page page-content" tkMainContent>
<page-router-outlet></page-router-outlet>
</StackLayout>
</RadSideDrawer>
My routing-module for the parent component looks like:
import {NgModule} from '#angular/core';
import {Routes} from '#angular/router';
import {NativeScriptRouterModule} from 'nativescript-angular/router';
import {HomeComponent} from './home.component';
import {DashboardComponent} from '../dashboard/dashboard.component';
import {Dashboard2Component} from '../dashboard2/dashboard.component';
const routes: Routes = [
{
path: '',
component: HomeComponent,
children: [
{path: '', component: DashboardComponent},
{path: 'projects', component: Dashboard2Component},
]
}
];
#NgModule({
imports: [NativeScriptRouterModule.forChild(routes)],
exports: [NativeScriptRouterModule]
})
export class HomeRoutingModule {
}
export const routedComponents = [
HomeComponent
];
However, when I route to /projects, it brings me to a new page with a new actionbar
It's my understanding that this is because page-router-outlet creates a new page, that being said, I want to be able to navigate back from one child component back to the previous child component. Is this possible in {N}?
Make the main template for your app as a component...
app-template.component.html:
<ActionBar class="action-bar">
<NavigationButton class="action-item" icon="res://ic_menu" (tap)="toggleDrawer()"></NavigationButton>
<StackLayout class="action-bar-title">
<Label [text]="title"></Label>
</StackLayout>
</ActionBar>
<RadSideDrawer showOverNavigation="true">
<ScrollView tkDrawerContent class="sidedrawer-center" tkDrawerClosed="onDrawerClosed" tkDrawerClosing="onDrawerClosing" tkDrawerOpened="onDrawerOpened" tkDrawerOpening="onDrawerOpening">
<StackLayout>
<StackLayout class="sidedrawer-header">
<Label text="blah blah blah" class="text-left text-capitalize font-weight-bold text-primary" textWrap="true"></Label>
</StackLayout>
<StackLayout class="sidedrawer-content">
<GridLayout (tap)="closeDrawer()" nsRouterLinkActive="active" [nsRouterLinkActiveOptions]="{exact: true}" [nsRouterLink]="['/']" class="sidedrawer-list-item" pageTransition="fade" rows="auto" columns="auto, *">
<Label class="sidedrawer-list-item-text" row="0" col="1" text="Home"></Label>
</GridLayout>
<GridLayout (tap)="closeDrawer()" nsRouterLinkActive="active" [nsRouterLink]="['/settings']" class="sidedrawer-list-item" pageTransition="fade" rows="auto" columns="auto, *">
<Label class="sidedrawer-list-item-text" row="0" col="1" text="Settings"></Label>
</GridLayout>
</StackLayout>
</StackLayout>
</ScrollView>
<StackLayout tkMainContent class="page">
<ng-content></ng-content>
</StackLayout>
</RadSideDrawer>
app-template.component.ts:
import { Component, ViewChild, Input, OnInit } from "#angular/core";
import { RadSideDrawer, PushTransition } from "nativescript-telerik-ui/sidedrawer";
import { RadSideDrawerComponent } from "nativescript-telerik-ui/sidedrawer/angular";
#Component({
selector: "app-template",
moduleId: module.id,
templateUrl: './app-template.component.html'
})
export class AppTemplateComponent {
canToggleDrawer: boolean = true;
constructor() { }
#Input() title: string = 'MyApp';
#ViewChild(RadSideDrawerComponent) drawerComponent: RadSideDrawerComponent;
drawer: RadSideDrawer;
ngOnInit() {}
ngAfterViewInit() {
this.drawer = this.drawerComponent.sideDrawer;
this.drawer.drawerTransition = new PushTransition();
}
toggleDrawer() {
if (this.canToggleDrawer) {
this.drawer.toggleDrawerState();
}
}
closeDrawer() {
this.drawer.closeDrawer();
}
}
then on your child views, just call it as <app-template> and put your content inside it. This way, you can keep your drawer menu in one file only, and still be able to use the back buttons...

nativescript template listview out put is not correct

anything wrong from there?
<GridLayout rows="*">
<ListView [items]="tabs">
<ng-template let-item="tab">
<Label [text] = "tab.name"></Label>
</ng-template>
</ListView>
</GridLayout>
And in my controller:
this.requestService.get('api/config/tabs')
.subscribe((data:any)=>{
this.zone.run(() => {
var results = [];
data.forEach(item=>{
results.push({name: item.attributes[0].value , id: item.attributes[0].id });
});
this.tabs = results;
});
});
But why the output is only [Object, Object]
There is a syntax error in the code
Change this
<ng-template let-item="tab">
to this
<ng-template let-tab="item">

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}}"

Resources