How to get context from NativeScript modal opener through frame? - nativescript

I'm unable get showModal to pass context through a frame to the default page. Also, showingModally does not seem to fire when modal is loaded (I guess args from showingModally should pass context?).
I've tried the example provided on https://docs.nativescript.org/ui/modal-view under Custom Actionbar, because I need the modal to load within a frame. The modal opens and closes fine, but showingModally in the modal XML does not seem to run.
home/home-page.js
const modalView = "home/modal-root";
function openModal(args) {
console.log('Opens modal');
const mainpage = args.object.page;
const context = "some context";
mainpage.showModal(modalView, context, () => {
console.log('Modal closed');
}, true);
}
exports.openModal = openModal;
home/home-page.xml
<Page xmlns="http://www.nativescript.org/tns.xsd">
<Page.actionBar>
<ActionBar title="Modal view Navigation" />
</Page.actionBar>
<GridLayout rows="auto, *">
<Button text="Open modal" tap="openModal" textWrap="true" />
</GridLayout>
</Page>
home/modal-root.xml
<Frame defaultPage="home/modal-view-page" />
home/modal-view-page.js
function onShowingModally(args) {
console.log("onShowingModally");
}
exports.onShowingModally = onShowingModally;
function onCloseModal(args) {
args.object.closeModal();
}
exports.onCloseModal = onCloseModal;
home/modal-view-page.xml
<Page backgroundColor="green" showingModally="onShowingModally">
<Page.actionBar>
<ActionBar backgroundColor="red" title="Modal view" icon="">
</ActionBar>
</Page.actionBar>
<StackLayout backgroundColor="lightGreen">
<Label text="Modal view with ActionBar" style="text-align:center;" textWrap="true" />
<Button text="Close Modal" tap="onCloseModal" />
</StackLayout>
</Page>
I've added the example to https://play.nativescript.org/?template=play-js&id=lFxTi4&v=9
with console logging.
Console show "Opens modal" on open and "Modal closed" on close as expected (from home/home-page.js), but "onShowingModally" (home/modal-view-page.js) nevers shows in console.

I think the docs need to be fixed. When you have the Frame being shown modally, the event will be called on the Frame not on the page within. If you are showing a simple view instead of Frame, then the event should be called on the view, basically whatever is the root view for your modal and in your example it's the Frame.
Updated Playground

Related

How to have some pages without bottom navigation?

I want to have bottom navigation in my app as described on this page but I also have a login page which should not have the bottom navigation tabs. So as the user enters the app, he's shown the login page and after login he should get to the first tab of the bottom navigation. The docs do not mention how this would be achieved. How can this be done?
Attempt 1:
app.js
let startPage = 'login/login-page';
if(settings.hasKey('token')){
startPage = 'app-root';
}
application.run({ moduleName: startPage });
This does take me to the login page but then nothing works to get me to app-root or another similar page that could house a BottomNavigation
Attempt 2:
On login page, nothing happens.
app.js
application.run({ moduleName: 'app-root' });
app-root.xml
<Frame defaultPage="login/login-page"></Frame>
login/login-page.js
//send email and password. check if its correct. send user forward on authentication
page.frame.navigate({
moduleName: 'router/router-page',
clearHistory: true
});
router/router-page.xml
<BottomNavigation id="bottomNav" automationText="tabNavigation" selectedIndex="0">
<TabStrip>
<TabStripItem>
<Image src="font://" class="mdi"></Image>
<Label text="Products"></Label>
</TabStripItem>
<TabStripItem>
<Image src="font://" class="mdi"></Image>
<Label text="Gallery"></Label>
</TabStripItem>
</TabStrip>
<TabContentItem>
<Frame id="main" defaultPage="categories/categories-page"></Frame>
</TabContentItem>
<TabContentItem>
<Frame id="gallery" defaultPage="gallery/gallery-page"></Frame>
</TabContentItem>
</BottomNavigation>
//note there's no router-page.js
//is it necessary?
This exact scenario can be found in this post:
https://www.nativescript.org/blog/implementing-a-login-for-nativescript-apps-with-tab-based-navigation
The repo (https://github.com/NativeScript/login-tab-navigation-ng) is using TabView but you can quickly change that to use the newer BottomNavigation instead.
Here's how I finally did it
app.js
application.run({ moduleName: 'app-root' });
app-root.xml
<Page actionBarHidden="true" loaded="onLoaded" xmlns="http://schemas.nativescript.org/tns.xsd">
<Frame id="rootframe" defaultPage="{{ defaultPage }}"></Frame>
</Page>
app-root.js
const Observable = require("#nativescript/core/data/observable").fromObject;
const settings = require("#nativescript/core/application-settings");
exports.onLoaded = function(args){
const page = args.object;
page.bindingContext = Observable({
defaultPage: (settings.hasKey('token')) ? 'router/router-page' : 'login/login-page'
})
}
router/router-page.xml
<Page actionBarHidden="true" loaded="onLoaded" class="page" xmlns="http://schemas.nativescript.org/tns.xsd">
<BottomNavigation id="bottomNav" automationText="tabNavigation" selectedIndex="0" selectedIndexChanged="onSelectedIndexChanged">
<TabStrip>
<TabStripItem>
<Image src="font://" class="mdi"></Image>
<Label text="Products"></Label>
</TabStripItem>
<TabStripItem>
<Image src="font://" class="mdi"></Image>
<Label text="Gallery"></Label>
</TabStripItem>
</TabStrip>
<TabContentItem>
<Frame id="main" defaultPage="categories/categories-page"></Frame>
</TabContentItem>
<TabContentItem>
<Frame id="gallery" defaultPage="gallery/gallery-page"></Frame>
</TabContentItem>
</BottomNavigation>
</Page>

MasterDetail with Prism, how to?

I am trying to test and example with MasterDetail. You can see the code in:
https://github.com/jrariasf/MD8/tree/master/MD8
The Master have 5 buttons to access to 4 detail pages (Home, MainPage, ViewA, ViewB and ViewC).
From ViewA, with 2 buttons I am able to load ViewB and ViewC
But I am not able to do that pushing a button in hambubrger menu then load the detail page adequated.
It only works if I put an absolute path in the CommandParameter in "PrismMasterDetailPage.xaml":
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="MD8.Views.PrismMasterDetailPage">
<MasterDetailPage.Master>
<ContentPage Title="Menu">
<StackLayout Padding="20">
<!-- TODO: // Update the Layout and add some real menu items -->
<Button Text="Home" Command="{Binding NavigateCommand}" CommandParameter="/PrismMasterDetailPage/NavigationPage/ViewA" />
<Button Text="MainPage" Command="{Binding NavigateCommand}" CommandParameter="/NavigationPage/MainPage" />
<Button Text="ViewA" Command="{Binding NavigateCommand}" CommandParameter="../ViewA" />
<Button Text="ViewB" Command="{Binding NavigateCommand}" CommandParameter="./ViewB" />
<Button Text="ViewC" Command="{Binding NavigateCommand}" CommandParameter="ViewC" />
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</MasterDetailPage>
Then, in "PrismMasterDetailPageViewModel.cs"
void ExecuteCommandName(string page)
{
Console.WriteLine("PrismMasterDetailPageViewModel - ExecuteCommandName() Vamos a {0}", page);
_navigationService.NavigateAsync(page);
}
If I am in "/PrismMasterDetailPage/NavigationPage/ViewA", what I have to do to unload ViewA and load ViewB ?
For example, in App.xaml.cs the code is:
await NavigationService.NavigateAsync("PrismMasterDetailPage/NavigationPage/ViewA");
Then, execute the app on android emulator, push buttons of hamburger menu and the result it is not as I expected.
Pressing Home button, the _navigationService.GetNavigationUriPath() returns: /PrismMasterDetailPage/NavigationPage/ViewA/NavigationPage?useModalNavigation=true/ViewA
why?
If I press button ViewA or ViewB or ViewC, it don't show anything. But the OnNavigatedFrom() method is called on each View*ViewModel.cs
What is wrong?
Thanks!!
One fo the first things you need to do is understand where you are Navigating from. Navigation in Xamarin.Forms is very dependent on where you are Navigating from. Remember that without Prism you would do something like this:
var mdp = new MyMasterDetailPage
{
Detail = new NavigationPage(new ViewA)
};
In order to achieve a Hamburger Menu with Prism you typically would want a MasterDetailPage as the Application's Main Page. The next segment in the Navigation Uri must be a NavigationPage and the following page would generally be a ContentPage.
<Button Text="Home"
Command="{Binding NavigateCommand}"
CommandParameter="/PrismMasterDetailPage/NavigationPage/ViewA" />
Ok so looking at this first one, this is generally what you would use when navigating from the PrismApplication which is why it works.
<Button Text="MainPage"
Command="{Binding NavigateCommand}"
CommandParameter="/NavigationPage/MainPage" />
Looking at this one, this is really close to what you do want except you are doing an absolute navigation which means that you are resetting the Application.MainPage. What you actually need is a relative Uri because you are navigating from the MasterDetailPage.
<Button Text="ViewA"
Command="{Binding NavigateCommand}"
CommandParameter="../ViewA" />
This is completely wrong because ../{path} is only supported from within a NavigationPage and you are in a MasterDetailPage...
<Button Text="ViewB"
Command="{Binding NavigateCommand}"
CommandParameter="./ViewB" />
This is just not supported by Prism at all.
<Button Text="ViewC"
Command="{Binding NavigateCommand}"
CommandParameter="ViewC" />
This is setting the MasterDetailPage.Detail like:
mdp.Detail = new ViewC()
Of course as I mentioned above you need it to be
mdp.Detail = new NavigationPage(new ViewC());
Thanks a lot Dan Siegel ! (#Dan S.)
I have just seen your comment, a month ago, sorry.
I was a newbie in C# and Xamarin and Prism and I am still a newbie but less :-)
I have learnt and tested a lot in the last weeks and I understand better the concepts.
After reading you I know that ../{path} is only valid with a NavigationPage .
Futhermore, one problem I had was thar I was checking the value of "_navigationService.GetNavigationUriPath()" inside OnNavigatedTo method and as I read later, at that moment the UriPath is not the real final UriPath.
In my code I wrote:
public void OnNavigatedTo(INavigationParameters parameters)
{
Console.WriteLine("DEBUG - ViewAVM: We are in {0}",
_navigationService.GetNavigationUriPath());
}
I moved the GetNavigationUriPath() call to another method and the results were as I expected.
Thanks!!

Nativescript Vue: Why does position of code matter in a Dock Layout matter?

Why does this following code produce this result: https://imgur.com/a/lQhLs8o ?
However, if I move the BottomNavigatorBar component to top position before CountryListComponent, it produces the desired result that looks like this: https://imgur.com/a/23z7bb2 ?
<template>
<Page actionBarHidden="true">
<DockLayout height="100%">
// first
<CountryListComponent dock="top">
// second
<BottomNavigationBar dock="bottom" activeColor="pink"
inactiveColor="yellow"
backgroundColor="black"
verticalAlignment="bottom"
#tabSelected="this.changeTab"
row="1">
<BottomNavigationTab title="Fiaarst" icon="icon-29.png" />
<BottomNavigationTab title="Second" icon="icon-29.png" />
<BottomNavigationTab title="Third" icon="icon-29.png" />
</BottomNavigationBar>
</DockLayout>
</Page>
</template>
CountryListComponent
<template>
<StackLayout backgroundColor="blue">
</StackLayout>
</template>
Refer the DockLayout documentation, by default stretchLastChild will be true which means BottomNavigationBar will take entire space if it's last child and vice versa.

Is it possible to use TabView Navigation within SideDrawer Navigation

I need to implement layout like in Twitter app, where the app has TabView for primary information and SideDrawer for supplementary information.
If I try to init RadSideDrawer mainContent with TabView and Frame I got empty screen:
...
<nsDrawer:RadSideDrawer.mainContent>
<TabView androidTabsPosition="bottom">
<TabViewItem class="tab-view-item" title="catalog">
<Frame defaultPage="pages/catalog/catalog-page"></Frame>
</TabViewItem>
</TabView>
</nsDrawer:RadSideDrawer.mainContent>
...
But if I init RadSideDrawer mainContent with Frame I got correct page:
...
<nsDrawer:RadSideDrawer.mainContent>
<Frame defaultPage="pages/catalog/catalog-page"></Frame>
</nsDrawer:RadSideDrawer.mainContent>
...
What it the correct way to combine TabView Navigation and SideDrawer Navigation?
I suspect the Frame should be wrapped within TabViewItem.view and TabViewItem must be wrapped by <TabView.items>
<nsDrawer:RadSideDrawer.mainContent>
<TabView androidTabsPosition="bottom">
<TabView.items>
<TabViewItem class="tab-view-item" title="catalog">
<TabViewItem.view>
<Frame defaultPage="pages/catalog/catalog-page"></Frame>
</TabViewItem.view>
</TabViewItem>
</TabView.items>
</TabView>
</nsDrawer:RadSideDrawer.mainContent>

How to use telerik ui plug in for side drawer in NativeScript app

I'm using Telerik UI plugin in my NativeScript app for implementing drawer functionality.I'm following this tutorial for this - https://www.nativescript.org/blog/details/using-cross-platform-native-sidedrawer-component-in-nativescript
Here is my Code-
import {Component} from "#angular/core";
import listViewModule = require("nativescript-telerik-ui/listview");
import drawerModule = require("nativescript-telerik-ui/sidedrawer");
#Component({
selector: "my-app",
template: `
<drawer:SideDrawer id="drawer1">
<drawer:SideDrawer.mainContent>
<!-- Place your page content here -->
<StackLayout>
<Label text="Tap the button" class="title"></Label>
<Button text="TAP" (tap)="onTap()"></Button>
<Label [text]="message" class="message" textWrap="true"></Label>
</StackLayout>
<StackLayout>
<Button tap="openDrawer" text="ToggleDrawer"/>
</StackLayout>
</drawer:SideDrawer.mainContent>
<drawer:SideDrawer.drawerContent>
<StackLayout cssClass="drawerContent">
<StackLayout cssClass="headerContent">
<Label text="Drawer Header"/>
</StackLayout>
<StackLayout cssClass="drawerMenuContent">
<Label text="Item 1"/>
<Label text="Item 2"/>
<Label text="Item 3"/>
<Label text="Item 4"/>
</StackLayout>
</StackLayout>
</drawer:SideDrawer.drawerContent>
</drawer:SideDrawer>
`,
})
export class AppComponent {
public counter: number = 16;
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--;
}
}
When I'm running this in my Emulator I'm getting a blank page. I'm using AngularJS2 with typescript & totally new to both these tools. So, can't able to understand what's I'm doing wrong.
Need some Guide.
nativescript-telerik-ui\listview and sidedrawer was before 2.0.0-rc.1 so they are currently working on it you can check this link - https://github.com/telerik/nativescript-ui-samples-angular/issues/1#issuecomment-225791969
if you are using beta version of angular then check this link to repository - https://github.com/telerik/nativescript-ui-samples-angular/tree/release/sdkAngular/app/sidedrawer

Resources