Dynamic GridLayout in nativescript with angular 2 - nativescript

I'm very new to Nativescript and Angular and it's my first stab at mobile app development, so please bear with me.
I'm trying to create a basic app with Angular2 and Nativescript. What I'm trying to do, is to display 3 buttons at the bottom of the screen when the app starts (these are created dynamically on initialisation). Once you click any of these buttons, I want this row to be moved up and a second row of buttons to appear. The transition should be animated.
In the first approach I tried I couldn't add the GridLayout to my DockLayout.
The home.html file
<DockLayout #container stretchLastChild="false">
</DockLayout>
the home.component.ts file snippet
export class HomePage implements OnInit {
#ViewChild("container") container: DockLayout;
constructor(private _router: Router, private page: Page) {
var view = new View();
}
ngOnInit() {
this.page.actionBarHidden = true;
this.page.backgroundImage = this.page.ios ? "res://background_large_file.jpg" : "res://background_large_file";
console.dir(this.container);
//Create the grid layout on the page
var bottomNav = new GridLayout();
//Add the GridLayout Columns
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
bottomNav.addColumn(new ItemSpec(1, GridUnitType.auto));
//Add the GridLayout Row
bottomNav.addRow(new ItemSpec(1, GridUnitType.auto));
//Create the buttons
var FButton = new Button();
FButton.text = "F";
var BButton = new Button();
BButton.text = "B";
var CButton = new Button();
CButton.text= "C";
//Position the buttons
FButton.set('row', '0');
FButton.set('col', '0');
BButton.set('row', '0');
BButton.set('col', '1');
CButton.set('row', '0');
CButton.set('col', '2');
bottomNav.addChild(FButton);
bottomNav.addChild(BButton);
bottomNav.addChild(CButton);
//Attempt to add to DockLayout container
--This is the part I can't get working
}
}
The other approach I was considering would be to hardcode my template and only show the first row. On tap of one of the buttons in the first row I'd move it up and fade in the second row. I couldn't get the reference to the GridLayouts on the page to allow me to do this. I'm thinking I might need to use "let container = this.container;" but am a bit unsure on this.
The home.html file
<DockLayout #container stretchLastChild="false">
<GridLayout #second_row columns="*, *, *" rows="auto" dock="bottom" class="choices hidden">
<Button text="M" row="1" col="0"></Button>
<Button text="F" row="1" col="1"></Button>
<Button text="D" row="1" col="2"></Button>
</GridLayout>
<GridLayout #initial_row columns="*, *, *" rows="auto" dock="bottom" class="choices initial">
<Button text="F" row="0" col="0" (tap)="showF()"></Button>
<Button text="B" row="0" col="1" (tap)="showB()"></Button>
<Button text="C" row="0" col="2" (tap)="showC()"></Button>
</GridLayout>
</DockLayout>
The home.component.ts file
export class HomePage implements OnInit {
#ViewChild("container") container: DockLayout;
constructor(private _router: Router, private page: Page) {
var view = new View();
}
ngOnInit() {
this.page.actionBarHidden = true;
}
showC() {
let container = <View>this.container;
container.animate({
opacity: 1,
duration:200
});
console.log('in here');
}
}
The final layout I'm aiming for is (any taps on the new row of buttons - #second_row - will navigate off to other pages once I set up on tap events and routing):
<DockLayout #container stretchLastChild="false">
<GridLayout #second_row columns="*, *, *" rows="auto" dock="bottom" class="choices hidden">
<Button text="M" row="1" col="0"></Button>
<Button text="F" row="1" col="1"></Button>
<Button text="D" row="1" col="2"></Button>
</GridLayout>
<GridLayout #initial_row columns="*, *, *" rows="auto" dock="bottom" class="choices initial">
<Button text="F" row="0" col="0" (tap)="showF()"></Button>
<Button text="B" row="0" col="1" (tap)="showB()"></Button>
<Button text="C" row="0" col="2" (tap)="showC()"></Button>
</GridLayout>
</DockLayout>
I would appreciate any help and advice on the subject.
Thanks in advance!

I would suggested you to use NativeScript Angular translate animation. With its help you could move up the first GridLayout and to show the next one. You could review my sample project here .In regard to that you could also review below attached articles.
http://docs.nativescript.org/angular/ui/animation.html
http://docs.nativescript.org/angular/tutorial/ng-chapter-4
http://docs.nativescript.org/angular/tutorial/ng-chapter-0

For your first example to add your new gridlayout to your page:
page.content = bottomNav;
Would do the trick.

Related

Nativescript Listview with header and scroll entire page

I am using ListView with Header portion on top of it like below,
<StackLayout>
<StackLayout height="200">
<Label text="Header content goes in this section"></Label>
<StackLayout>
<ListView [items]='posts'>
<!-- template items goes here -->
</ListView>
</StackLayout>
When we scroll to list the header is sticky in this case.
Is there a option that scroll overrides header also ?.I mean that header also part of scroll.
Fr Angular-2 application you can now use tkTemplateKey deirective and create your own headers, footers, groups and other custom list-view elements.
Example can be found here
Here is the code for a list-view with header and groups.
page.component.html
<ListView [items]="countries" [itemTemplateSelector]="templateSelector" (itemTap)="onItemTapFirstList($event)" class="list-group" separatorColor="white">
<ng-template nsTemplateKey="header" let-header="item">
<Label [text]="header.name" class="list-group-item h3 bg-primary" isUserInteractionEnabled="false" color="white" fontSize="24"></Label>
</ng-template>
<ng-template nsTemplateKey="footer" let-footer="item">
<Label [text]="footer.name" class="list-group-item" backgroundColor="gray"></Label>
</ng-template>
<ng-template nsTemplateKey="cell" let-country="item">
<StackLayout class="list-group-item">
<Label [text]="country.name" class="list-group-item-heading"></Label>
<Label [text]="country.desc" class="list-group-item-text" textWrap="true"></Label>
</StackLayout>
</ng-template>
</ListView>
page.component.ts
#Component({
moduleId: module.id,
templateUrl: "./multi-line-grouped.component.html",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MultiLineGroupedListViewExampleComponent implements OnInit {
public countries: Array<any> = [];
public templateSelector = (item: any, index: number, items: any) => {
return item.type || "cell";
}
ngOnInit() {
for (let i = 0; i < mockedCounties.length; i++) {
this.countries.push(mockedCounties[i]);
}
}
onItemTapFirstList(args: ItemEventData) {
console.log(args.index);
}
}
Not sure if there's another way, but one way could be moving the header inside the listview. For that to work it needs to be in the posts Array, so you may want to transform that into some sort of wrapping class that can contain eiter a header or item row. Then create two templates inside the listview that depending on the template key render a header or an item.
For details on templates, see https://docs.nativescript.org/cookbook/ui/list-view#define-multiple-item-templates-and-an-item-template-selector-in-xml
You can use *ngFor creating the list.Here is the sample code for doing this.
<ScrollView>
<StackLayout>
//define your header over here
<Label text="hey header"></Label>
<StackLayout *ngFor="let item of <Array>">
<GridLayout columns="4*,*" rows="*,">
<Label row="0" col="0" text="hey label"></Label>
</GridLayout>
<StackLayout>
<StackLayout>
</ScollView>

Is it possible to get children in custom component?

Is it possible to get children in custom component?
<myCustomComponent>
<Label #id1 text="ID 1"></Label>
<Label #id2 text="ID 2"></Label>
</myCustomComponent>
Is there a way to get the reference of these two Lable to get them positioned in a GridLayout in my custom component?
Apart from the option with getViewById() there are several options to see the a layout's children or their index and also to add, remove, insert and reset children.
e.g.
page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
<StackLayout id="st" class="p-20">
<Label id="lbl-title" 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>
</Page>
page.ts
let page = <Page>args.object;
let stack = <StackLayout>page.getViewById("st");
let label = <Label>page.getViewById("lbl-title");
console.log("page.content: " + page.content); // page.content: StackLayout<st>#file:///app/main-page.xml:19:5;
console.log("stack.getChildrenCount(): " + stack.getChildrenCount()) // 3
console.log("stack.getChildAt(1): " + stack.getChildAt(1)); // stack.getChildAt(1): Button(5)#file:///app/main-page.xml:21:9;
console.log("stack.getChildIndex(label): " + stack.getChildIndex(label)); // 0
console.log("stack.parent: " + stack.parent); // stack.parent: Page(1)#file:///app/main-page.xml:7:1;
var newlabel = new Label();
newlabel.text = "new Label";
stack.addChild(newlabel);
More about all supported methods here
The view class has a getViewById() method for this purpose.
In my-component.xml:
<StackLayout loaded="onLoaded">
<Label id="label1" text="ID 1" mycustomattribute="Hi there."></Label>
<Label id="label2" text="ID 2"></Label>
</StackLayout>
In my-component.js:
exports.onLoaded = function(args) {
let view = args.object;
let label1 = view.getViewById('label1');
console.log(label1.mycustomattribute);
// > "Hi there."
};

NativeScript: how to make view 'transparent' for any user interactions

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

Nativescript -Open SideDrawer on button click

I am using telerik ui for native script. I need a toggle button at top to open the side menu. but I am not able to call the Showdrawer() as per the docs.
What I need is on button click side menu should open. I tried calling RadSideDrawer.prototype.showDrawer(), but failed.
Is there any other side menu available for Nativescript?
main-page.xml
<Page xmlns="http://www.nativescript.org/tns.xsd" xmlns:drawer="nativescript-telerik-ui/sidedrawer" loaded="pageLoaded">
<Page.actionBar>
<ActionBar>
<android>
<NavigationButton text="Go Back" android.systemIcon="ic_menu_moreoverflow" tap="showSideDrawer" />
</android>
</ActionBar>
</Page.actionBar>
<drawer:RadSideDrawer>
<drawer:RadSideDrawer.mainContent>
<StackLayout>
<Label text="{{ mainContentText }}" textWrap="true" />
</StackLayout>
</drawer:RadSideDrawer.mainContent>
<drawer:RadSideDrawer.drawerContent>
<StackLayout cssClass="drawerContent" style="background-color:white;">
<StackLayout cssClass="headerContent">
<Label text="Header" />
</StackLayout>
<StackLayout cssClass="drawerMenuContent">
<Label text="Item 1" style="color:black;" />
<Label text="Item 2" style="color:black;" />
<Label text="Item 3" style="color:black;" />
<Label text="Item 4" style="color:black;" />
</StackLayout>
</StackLayout>
</drawer:RadSideDrawer.drawerContent>
</drawer:RadSideDrawer>
</Page>
getting-started-model.js
var observableModule = require("data/observable");
var GettingStartedViewModel = (function (_super) {
__extends(GettingStartedViewModel, _super);
function GettingStartedViewModel() {
_super.call(this);
this.set("mainContentText", "SideDrawer for NativeScript can be easily setup in the XML definition of your page by defining main- and drawer-content. The component"
+ " has a default transition and position and also exposes notifications related to changes in its state.");
}
return GettingStartedViewModel;
})(observableModule.Observable);
exports.GettingStartedViewModel = GettingStartedViewModel;
function showSideDrawer(args) {
console.log("Show SideDrawer tapped.");
// Show sidedrawer ...
_super.prototype.showDrawer.call(this);
}
exports.showSideDrawer = showSideDrawer;
main page.js
var viewModelModule = require("./getting-started-model");
function pageLoaded(args) {
console.log("Page loaded");
var page = args.object;
page.bindingContext = new viewModelModule.GettingStartedViewModel();
}
exports.pageLoaded = pageLoaded;
You can take a look at this SDK examples that show the main functionality of the RadSideDrawer. As mentioned by #R Pelzer all you need to do is get the instance of the RadSideDrawer for example by using its id:
import drawerModule = require("nativescript-telerik-ui-pro/sidedrawer");
import frameModule = require("ui/frame");
var sideDrawer: drawerModule.RadSideDrawer = <drawerModule.RadSideDrawer>(frameModule.topmost().getViewById("sideDrawer"));
and call its showDrawer() method:
sideDrawer.showDrawer();
Are you calling the showSideDrawer function from code you didn't post? Are you sure you linked the tap button?
<Button tap="showSideDrawer" text="ToggleDrawer"/>
Maybe you can try to give the sideDrawer an Id and use this code.
var drawer = frameModule.topmost().getViewById("sideDrawer");
drawer.showDrawer();
You are getting undefined because no id was assigned to the drawer so to fix your problem assign an id to the sideDrawer <drawer:RadSideDrawer id="sideDrawer"> then you can call
var frame = require('ui/frame');
var drawer = frame.topmost().getViewById("sideDrawer");
function showSideDrawer(){
drawer.showDrawer(); // i prefer using .toggleDrawerState();
};
In my case, I missed inserting , as a result, there was a missing component when toggleDrawer was being called hence the error "TypeError: Cannot read property 'toggleDrawerState' of undefined".
Try inserting all the body component of the xml file in this might solve the issue.
Happy coding :))

Find button or label inside Griview using Nativescript Grid view

I using GridView Nativescript to arrange the UI and I want to set css into specific button or label dynamically. How can I find inside label when outside button is trigger and I tried using getViewById method but always get empty result. Isn't got any method to find it ?
var gridview;
exports.onPageLoaded = function (args) {
page = args.object;
gridview = page.getViewById("gridview");
};
exports.onSelectedIndexChanged = function(args){
var totalMatch = 0;
var btn = args.object;
var index = btn.index;
var a = gridview.getViewById("25");
btn.backgroundColor = "red";
btn.color = "white";
};
<GridLayout columns="*,1,*,1,*" rows="auto" borderWidth="1" borderColor="#DBDBDB" borderRadius = "3" >
<Button text="selectALL" index="0" tap="onSelectedIndexChanged" backgroundColor= "red" id ="btnSelectAll" color="white" borderRadius="3" />
<Button text="UnselectAll" index="1" col="2" tap="onSelectedIndexChanged" backgroundColor= "white" id ="btnUnSelectAll" borderRadius = "3" />
<Border col="1" borderWidth="1" borderColor="#DBDBDB" />
<Border col="3" borderWidth="1" borderColor="#DBDBDB" />
</GridLayout>
<gv:GridView items="{{ items }}" verticalSpacing="3" horizontalSpacing="3" colWidth="100" rowHeight="50" padding="3" id="gridview" height="400">
<gv:GridView.itemTemplate>
<GridLayout backgroundColor="#ffffff" style="border-width:3px;border-color:#696969;border-radius:5">
<Button text="{{ Name }}" id="{{ Id }}" index="{{ Index }}" tap="onGridViewItemTap" backgroundColor= "white" color="red" style="background-size:100% 100%;background-repeat:no-repeat;background-image:url('~/images/drawable-hdpi-v4/spt_fiter_checked.png')" borderWidth="1" borderColor="#DBDBDB" borderRadius = "5"/>
</GridLayout>
</gv:GridView.itemTemplate>
</gv:GridView>
After my understanding, what you are trying to do is:
press the button in the GridLayout
trigger the onSelectedIndexChanged function
Access the button with id="25"
Modify the button
First, I would like to point out that it is not recommended to access views by ids, especially when you have a complex UI structure. I would suggest that you use bindings. I am not sure what exactly you would like to modify, so I will assume that you would like to change the background property of the button. In this case, you can bind an Observable object in the code to the background property in the XML. Then in the function onSelectedIndexChanged, you can modify the object in the code by setting a new color. Since it is observable, it will notify all of the listeners for the modification. And in our case - the property in the XML.
Here I am sending you a simple example:
main-page.xml
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onPageLoaded">
<GridLayout rows="auto,auto" >
<Button text="Click to Change Color" row="0" tap="onSelectedIndexChanged"/>
<Button text="Button" row="1" backgroundColor="{{color}}"/>
</GridLayout>
</Page>
main-page.js
var observable = require("data/observable");
var json={color:'red'};
var changeCss = new observable.Observable(json);
var gridview;
exports.onPageLoaded = function (args) {
page = args.object;
page.bindingContext=changeCss;
};
exports.onSelectedIndexChanged = function(args){
changeCss.set("color","blue");
};
I hope this will give you some directions.

Resources