I am building Ionic React application, and the version of ionic is 5. In my application, I have bottom navigation. I have mentioned the logic of bottom navigation in App.tsx.
I have an add button on the Dashboard page on clicking that button I want a page to open which will not contain the bottom navigation. I got many answers over the internet which is for Ionic-Angular. I am specifically looking for an answer Ionic-React.
App.tsx
<IonContent>
<IonReactRouter>
<IonTabs>
<IonRouterOutlet>
<Route path="/profile" component={Profile} exact={true} />
<Route path="/dashboard" component={Dashboard} exact={true} />
<Route path="/dashboard/addInfo" component={Info} exact={true} />
<Route path="/" render={() => <Redirect to="/dashboard" />} exact={true} />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="home" href="/home">
<IonIcon icon={home} />
<IonLabel>Home</IonLabel>
</IonTabButton>
<IonTabButton tab="profile" href="/profile">
<IonIcon icon={profile} />
<IonLabel>Profile</IonLabel>
</IonTabButton>
<IonTabButton tab="dashboard" href="/dashboard">
<IonIcon icon={grid} />
<IonLabel>Dashboard</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
</IonReactRouter>
</IonContent>
This might be what #MostafaHarb was trying to explain. You can have nested IonRouterOutlets, so place your tabs within a TabContainer component off your main App.tsx (shown here as a render prop on the /tabs Route). You will likely need to provide a fallback route, in this case there's a redirect to the tabs Route when the path is "/"
<IonReactRouter>
<IonRouterOutlet>
<Route path="/notabs" render={() => <div>a page no tabs</div>} />
<Route
path="/tabs"
render={() => (
<IonTabs>
<IonRouterOutlet>
<Route
path="/tabs/tab1"
component={Tab1}
exact={true}
/>
<Route
path="/tabs/tab2"
component={Tab2}
exact={true}
/>
<Route
path="/tabs/tab3"
component={Tab3}
exact={true}
/>
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab 1" href="/tabs/tab1">
<IonIcon icon={circle} />
<IonLabel>Tab1</IonLabel>
</IonTabButton>
<IonTabButton tab="tab 2" href="/tabs/tab2">
<IonIcon icon={square} />
<IonLabel>Tab2</IonLabel>
</IonTabButton>
<IonTabButton tab="tab 3" href="/tabs/tab3">
<IonIcon icon={triangle} />
<IonLabel>Tab3</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
)}
/>
<Route
path="/"
render={() => <Redirect to="/tabs" />}
exact={true}
/>
</IonRouterOutlet>
</IonReactRouter>
</IonApp>
);
Good luck!
I did something like this and that worked for me. I created an id for the tabs and then manipulated that to show or hide
function showTab() {
const tabBar = document.getElementById('appTabBar');
if (tabBar !== null) {
console.log("enabled")
tabBar.style.display = 'flex';
}
}
function hideTab() {
const tabBar = document.getElementById('appTabBar');
if (tabBar !== null) {
tabBar.style.display = 'none';
}
}
<IonTabBar slot="bottom" id="appTabBar" >
<IonTabButton tab="account" href="/account">
<IonIcon icon={personCircleOutline}/>
<IonLabel>Profile</IonLabel>
</IonTabButton>
</IonTabBar>
Related
I'm using Ionic with React.
In my app, I've both side menu and the tabs together.
In the tabs only the default tab is working properly other tabs are showing a blank screen.
My code is as follows:
1. App.tsx looks as follows
const App: React.FC = () => (
<IonApp>
<IonReactRouter>
<IonSplitPane contentId="main">
<Menu />
<IonRouterOutlet id="main">
<Route path="/login" component={Login} exact />
<Route path="/tabs" component={Tabs} exact />
<Route path="/register" component={Register} exact />
<Route exact path="/" render={() => <Redirect to="/login" />} />
</IonRouterOutlet>
</IonSplitPane>
</IonReactRouter>
</IonApp>
);
export default App;
In this, the below Route opens a tabbed page
<Route path="/tabs" component={Tabs} exact />
2. Tabs.tsx looks as follows
function Tabs() {
return (
<IonTabs>
<IonRouterOutlet>
<Route path="/tabs/tab1" component={Tab1} exact={true} />
<Route path="/tabs/tab2" component={Tab2} exact={true} />
<Route path="/tabs/tab3" component={Tab3} exact={true}/>
<Route path="/tabs" render={() => <Redirect to="/tabs/tab1" />} exact={true} />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="calendar" href="/tabs/tab1">
<IonIcon icon={calendar} />
<IonLabel>Tab1</IonLabel>
</IonTabButton>
<IonTabButton tab="analysis" href="/tabs/tab2">
<IonIcon icon={barChart} />
<IonLabel>Tab2</IonLabel>
</IonTabButton>
<IonTabButton tab="timer" href="/tabs/tab3">
<IonIcon icon={timer} />
<IonLabel>Tab3</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
);
};
export default Tabs;
3. Tab1.tsx looks like this
const Tab1 = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar color="secondary">
<IonButtons slot="start">
<IonMenuButton />
</IonButtons>
<IonTitle>Timesheet</IonTitle>
</IonToolbar>
</IonHeader>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Tab 1</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent class="ion-padding">
Tab 1 Content
</IonContent>
</IonPage>
);
};
export default Tab1;
Other Tabs Tab2.tsx and Tab3.tsx look exactly the same.
I figured out the issue.
It was an issue with the Route into my App.tsx file.
I changed my Route as below. (removed exact)
<Route path="/tabs" component={Tabs} />
Earlier Route which was causing the error is below
<Route path="/tabs" component={Tabs} exact/>
I want to display an ion-split-pane when the app is displayed on a desktop (or large screen)
And switch to an ion-tabswhen the app is displayed on a mobile.
Is this possible to do that ? I'm using Ionic on React
Here a non-perfect solution, because thes routes are duplicated.
I don't know how to fix this unfortunatly.
const Routes = () => <IonRouterOutlet id="main">
<Route path="/tabs" component={Tabs} exact={true} />
<Route path="/tab1" component={Tab1} exact={true} />
<Route path="/tab2" component={Tab2} exact={true} />
<Route path="/tab3" component={Tab3} />
<Route path="/" render={() => <Redirect to="/tab1" />} exact={true} />
</IonRouterOutlet>
const SplitPane = () => <IonSplitPane contentId="main">
<Menu />
<Routes />
</IonSplitPane>
const Tabs = () => <IonTabs>
<IonRouterOutlet id="main">
<Route path="/tabs" component={Tabs} exact={true} />
<Route path="/tab1" component={Tab1} exact={true} />
<Route path="/tab2" component={Tab2} exact={true} />
<Route path="/tab3" component={Tab3} />
<Route path="/" render={() => <Redirect to="/tab1" />} exact={true} />
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/tab1">
<IonIcon icon={triangle} />
<IonLabel>Tab 1</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/tab2">
<IonIcon icon={ellipse} />
<IonLabel>Tab 2</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
const App: React.FC = () => {
return (
<IonApp>
<IonReactRouter>
{isPlatform("mobileweb") && <Tabs />}
{isPlatform("desktop") && <SplitPane />}
</IonReactRouter>
</IonApp>
);
}
export default App;
Given #ionic/react restrictions this is what I came up with:
// Define routes as an array of components and add key prop
const routeComponents = [
<Redirect exact from="/" to={routes.dashboard} />,
<Route path={routes.dashboard} exact component={DashboardPage} />,
<Route path={routes.first} exact component={FirstPage} />,
<Route path={routes.second} exact component={SecondPage} />,
<Route component={NotFoundPage} />,
].map((Route, index) => ({ ...Route, key: index }))
const App: React.FC = () => {
const showSplitPane = useBreakpoint('lg')
return (
<IonApp>
<IonReactRouter>
{showSplitPane
?
<IonSplitPane contentId="main">
<Menu items={menuItems} />
<IonRouterOutlet id="main">
{routeComponents}
</IonRouterOutlet>
</IonSplitPane>
:
<TabsWrapper items={menuItems}>
<IonRouterOutlet id="main">
{routeComponents}
</IonRouterOutlet>
</TabsWrapper>
}
</IonReactRouter>
</IonApp>
)
}
I wish there was an easier way
I need to wrap the Tab components with a react-router Link compoenent. but considering the following code, when clicking on tabs yields no result. if it means the Tabs component must be direct parent of all Tab(s) component then how do I apply Link component?
<Tabs
value={tab}
onChange={(e, v) => setTab(v)}
>
<Link to={`${props.match.url}`}>
<Tab label="Content" />
</Link>
<Link to={`${props.match.url}/comment`}>
<Tab label="Comment" /
</Link>
<Link to={`${props.match.url}/create`}>
<Tab label="Create" />
</Link>
</Tabs>
you can provide Tab a component prop:
<Tabs
value={tab}
onChange={(e, v) => setTab(v)}
>
<Tab component={Link} to={`${props.match.url}`} label="Content" />
<Tab component={Link} to={`${props.match.url}/comment`} label="Comment" />
<Tab component={Link} to={`${props.match.url}/create`} label="Create" />
</Tabs>
I am trying to develop an application where I have to download and display an image from a website. THis is the code snippet which I have used:-
LoadImage(){
const transfer = new Transfer();
var url = "http://demo.observerjobs.lk/storage/public/uploads/vacancies/"+this.hash+"/"+this.attachment;
var uri = encodeURI(url);
var filepath = cordova.file.cacheDirectory+ '/' + this.vacancy.attachment;//("/"+this.vacancy.attachment);
transfer.download(uri, filepath, true ).then((entry) => {
console.log('download complete: ' + entry );
this.image = "<ion-img src= '"+ entry.toURL() + "'/>";
console.log(this.image);
}).catch(error => {
console.log(JSON.stringify(error));
});
}
I create the img tag in the following format:-
"<ion-img src= 'file:///data/user/0/package_name/cache/suo-01-29-012588-12x3-kbd.jpg'/>"
However, I am unable to retrieve this file to be displayed. What Have I done wrong, and what can I do to remedy this?
Edit:- Here is the config.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<widget id="OBSCURED FOR IDENTITY" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>OBSCURED</name>
<description>An awesome Ionic/Cordova app.</description>
<author email="hi#ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author>
<content src="index.html"/>
<access origin="*"/>
<allow-intent href="http://*/*"/>
<allow-intent href="https://*/*"/>
<allow-intent href="tel:*"/>
<allow-intent href="sms:*"/>
<allow-intent href="mailto:*"/>
<allow-intent href="geo:*"/>
<platform name="android">
<allow-intent href="market:*"/>
<icon src="resources\android\icon\drawable-ldpi-icon.png" density="ldpi"/>
<icon src="resources\android\icon\drawable-mdpi-icon.png" density="mdpi"/>
<icon src="resources\android\icon\drawable-hdpi-icon.png" density="hdpi"/>
<icon src="resources\android\icon\drawable-xhdpi-icon.png" density="xhdpi"/>
<icon src="resources\android\icon\drawable-xxhdpi-icon.png" density="xxhdpi"/>
<icon src="resources\android\icon\drawable-xxxhdpi-icon.png" density="xxxhdpi"/>
<splash src="resources\android\splash\drawable-land-ldpi-screen.png" density="land-ldpi"/>
<splash src="resources\android\splash\drawable-land-mdpi-screen.png" density="land-mdpi"/>
<splash src="resources\android\splash\drawable-land-hdpi-screen.png" density="land-hdpi"/>
<splash src="resources\android\splash\drawable-port-ldpi-screen.png" density="port-ldpi"/>
<splash src="resources\android\splash\drawable-port-mdpi-screen.png" density="port-mdpi"/>
<splash src="resources\android\splash\drawable-port-hdpi-screen.png" density="port-hdpi"/>
</platform>
<platform name="ios">
<allow-intent href="itms:*"/>
<allow-intent href="itms-apps:*"/>
</platform>
<platform name="windows">
<preference name="windows-target-version" value="10.0"/>
</platform>
<preference name="webviewbounce" value="false"/>
<preference name="UIWebViewBounce" value="false"/>
<preference name="DisallowOverscroll" value="true"/>
<preference name="android-minSdkVersion" value="16"/>
<preference name="BackupWebStorage" value="none"/>
<preference name="SplashMaintainAspectRatio" value="true"/>
<preference name="FadeSplashScreenDuration" value="300"/>
<preference name="SplashScreen" value="screen"/>
<preference name="SplashScreenDelay" value="3000"/>
<feature name="StatusBar">
<param name="ios-package" onload="true" value="CDVStatusBar"/>
</feature>
<plugin name="ionic-plugin-keyboard" spec="~2.2.1"/>
<plugin name="cordova-plugin-whitelist" spec="1.3.1"/>
<plugin name="cordova-plugin-console" spec="1.0.5"/>
<plugin name="cordova-plugin-statusbar" spec="2.2.1"/>
<plugin name="cordova-plugin-device" spec="1.1.4"/>
<plugin name="cordova-plugin-splashscreen" spec="~4.0.1"/>
<icon src="resources\android\icon\drawable-xhdpi-icon.png"/>
<allow-navigation href="http://192.168.1.3:8100"/>
<allow-navigation href="http://192.168.1.3:8101"/>
<allow-navigation href="http://192.168.1.2:8100"/>
<allow-navigation href="http://192.168.8.105:8100"/>
</widget>
Here is the http file of the relevant page:-
<!--
Generated template for the Vacancy page.
See http://ionicframework.com/docs/v2/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<ion-navbar>
<ion-title>Vacancy</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list no-lines *ngIf="vacancy">
<ion-item >Contract Type : {{vacancy.value}}</ion-item>
<ion-item >Company : {{item.company_name}}</ion-item>
<ion-item >Deadline : {{ deadlinetime }}</ion-item>
<ion-item >Job ID : #JD{{ item.id }}</ion-item>
</ion-list>
<h2 *ngIf="vacancy" orientation="center">{{item.job_title}}</h2>
<div *ngIf="image" [innerHtml]="image"></div>
</ion-content>
I would try:
Component:
imageSrc:string//class variable
LoadImage(){
const transfer = new Transfer();
var url = "http://demo.observerjobs.lk/storage/public/uploads/vacancies/"+this.hash+"/"+this.attachment;
var uri = encodeURI(url);
var filepath = cordova.file.cacheDirectory+ '/' + this.vacancy.attachment;//("/"+this.vacancy.attachment);
transfer.download(uri, filepath, true ).then((entry) => {
console.log('download complete: ' + entry );
this.imageSrc = entry.toUrl();
console.log(this.imageSrc);
}).catch(error => {
console.log(JSON.stringify(error));
});
}
HTML:
<img *ngIf="imageSrc" [src]="imageSrc"></img>
I'm using a tabView-Element of primefaces. The first tab points to the "Home" element the second one to a registration form. The registration form is validated. The problem is, when the form is validated the user is redirect to the home tab. When the user navigates back, he can see the validation errors.
This seems to be a more or less common problem, but I did not find a proper solution. I tried out Oleg Varaksin suggestion to prevent a user switching tabs when a form validation failed, but my problem starts earlier.
I tried to fiddle aorund with the onTabChange event but with no success. Is there a mistake in my code or how can I approach may probelm? I would welcome a push in the right direction.
<p:tabView id="mainTabView">
<p:tab title="Startseite" id="startPage">
<p:layout style="min-width:400px;min-height:400px;border:none !important"
id="layout_login">
<p:layoutUnit position="west" size="305">
<p:panel>
<h:form id="loginForm">
<p:panelGrid columns="2" styleClass="gridWithNoBorders reducedFontSize">
<p:outputLabel value="Benutzername:" />
<p:inputText size="14" value="#{data.username}" />
<p:outputLabel value="Passwort:" />
<p:inputText size="14" value="#{data.loginPassword}" />
</p:panelGrid>
<p:separator />
<h:commandButton styleClass="reducedFontSize" value="Anmelden" />
</h:form>
</p:panel>
</p:layoutUnit>
<p:layoutUnit position="center">
<p:panel>
Lorem ipsum dolor sit amet(...)
</p:panel>
</p:layoutUnit>
</p:layout>
</p:tab>
<p:tab id="registrationPage" title="#{ivy.cms.co('/Translations/Registration/tabTitleRegistration')}" >
<h:form id="registrationForm">
<p:panel header="#{ivy.cms.co('/Translations/Registration/panelHeaderRegistration')}">
<p:panelGrid columns="3">
<p:outputLabel value="#{ivy.cms.co('/Translations/Registration/labelEmail')}" />
<p:inputText value="#{data.email}" id="email1" required="true" requiredMessage="#{ivy.cms.co('/Translations/Registration/enterEmailAddress')}">
<f:validator validatorId="EmailValidator" />
<f:validator validatorId="EmailCompareValidator" />
</p:inputText>
<p:message for="email1" />
<p:outputLabel value="#{ivy.cms.co('/Translations/Registration/labelRepeatEmail')}" />
<p:inputText value="#{data.emailConfirm}" id="email2" required="true" requiredMessage="#{ivy.cms.co('/Translations/Registration/repeatMailAddress')}">
<f:validator validatorId="EmailValidator" />
</p:inputText>
<p:message for="email2"/>
<p:outputLabel value="#{ivy.cms.co('/Translations/Registration/labelPrename')}" />
<p:inputText value="#{data.prename}" id="prename" required="true" requiredMessage="#{ivy.cms.co('/Translations/Registration/enterPrename')}"/>
<p:message for="prename"/>
<p:outputLabel value="#{ivy.cms.co('/Translations/Registration/labelSurname')}" />
<p:inputText value="#{data.surname}" id="surname" required="true" requiredMessage="#{ivy.cms.co('/Translations/Registration/enterSurname')}"/>
<p:message for="surname"/>
<p:outputLabel value="#{ivy.cms.co('/Translations/Registration/labelPassword')}" />
<p:password id="pwd1" value="#{data.registrationPassword}" match="pwd2"
required="true" feedback="true" requiredMessage="#{ivy.cms.co('/Translations/Registration/enterPassword')}"
weakLabel="#{ivy.cms.co('/Translations/Registration/passwordWeak')}"
goodLabel="#{ivy.cms.co('/Translations/Registration/passwordMedium')}"
strongLabel="#{ivy.cms.co('/Translations/Registration/passwordStrong')}" />
<p:message for="pwd1"/>
<p:outputLabel value="#{ivy.cms.co('/Translations/Registration/labelRepeatPassword')}" />
<p:password id="pwd2" value="#{data.confirmPassword}" required="true" requiredMessage="#{ivy.cms.co('/Translations/Registration/repeatPassword')}"/>
<p:message for="pwd2"/>
<h:commandButton value="#{ivy.cms.co('/Translations/Registration/finishRegistration')}" actionListener="#{logic.startRegistration}" >
<p:ajax event="click" listener="#{logic.startRegistration}"/>
</h:commandButton>
</p:panelGrid>
</p:panel>
</h:form>
</p:tab>
</p:tabView>
I had to combine the binding and ajax to achieve my goal. It seems that the activeIndex is resetted after the form validation (?) therefore it is not honored after submitting the form. I've used the approach that was suggested by Steves to use the binding property. So here is the working code:
xhtml:
<p:layoutUnit position="center">
<p:tabView id="mainTabView" binding="#{tabIndexHelper.messagesTab}">
<p:ajax event="tabChange" listener="#{tabIndexHelper.onTabChange}" />
<p:tab title="Startseite" id="startPage">
<ui:include src="HomePage.xhtml" />
</p:tab>
<p:tab id="registrationPage" title="#{ivy.cms.co('/Translations/Registration/tabTitleRegistration')}" >
<ui:include src="RegisterPage.xhtml" />
</p:tab>
</p:tabView>
</p:layoutUnit>
And the backing bean:
private TabView messagesTab = new TabView();
public TabView getMessagesTab () {
return messagesTab;
}
public void setMessagesTab(TabView messagesTab ) {
this.messagesTab = messagesTab;
}
public void onTabChange(TabChangeEvent event) {
TabView tabView = (TabView) event.getComponent();
int activeIndex = tabView.getChildren().indexOf(event.getTab());
this.messagesTab.setActiveIndex(activeIndex);
}
Thanks for support. :)
You need to fix the current tab selected if you don't the update or validation should reset the tabview with default tab selected(the first one).
So you could use activeIndex attribut from tabView :
<p:tabView activeIndex="#{bean.activeIndex}"/>
From Primeface User Guide:
activeIndex is an Integer with default value 0. Index of the active tab.
And create an event for set this var like that :
<p:ajax event="tabChange" listener="#{bean.onTabChange}"/>