Ionic 4 — Hiding & Showing Tabs on certain pages

Ionic 4 has come with a number of changes. The biggest change, in my opinion, is how we utilize Tabs and Navigation. In ionic 3 we could add an attribute to certain tabs called tabsHideOnSubPages which allowed us to do exactly that — hide the tab bar on subpages of that tab. Unfortunately, that has not been implemented for Ionic 4 as of yet, though it has been opened as a feature request.


So what should we do for Ionic 4?

Ionic 4 uses Angular’s routing system, instead of the Ionic 3 method of navController. We’ll be leveraging this change, to allow us to pragmatically hide the tab bar on pages of our choice while leaving it visible everywhere else.

Let's get started

To start, we’ll need to generate a new global service provider. This is done for us by default in Ionic 4, through the use of the providedIn: 'root'attribute.

We’ll create the service provider by running the command ionic g service core/tabs. After the command finishes, you’ll notice a new folder in your src/app/ folder, with our new service provider, called tabs.service.ts.

Next, we’ll need to import the service into our app.modules.ts provider array, as well as importing it into our app.component.ts file, in our constructor.

app.module.ts

@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
...
],
providers: [
TabsService
...
],
bootstrap: [AppComponent],
})
export class AppModule {
}

app.component.ts

@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
})
export class AppComponent {
constructor(public tabs: TabsService) {
...
}
...
}

Configure the TabsService provider

When you first open up the new service provider, you should see the default template below.

import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class TabsService {

constructor() { }
}

Next, we’ll create a new private variable called hideTabBarPages. This is a string array, that we’ll use to tell Ionic what pages to hide our tab bar on. As you can see, I want to hide the tab bar on a page called new-group. But you can change this to include whatever you want.

private hideTabBarPages: string[] = [
'new-group',
];

Next, in your constructor, you should import the Router and the Platform providers like so:

constructor(private router: Router, private platform: Platform) {
this.platform.ready().then(() => {
this.navEvents();
});
}

All this does is wait for the platform to be ready before we do anything, as we can’t hide tabs that aren’t created yet, now can we?

Now we’ll go ahead and hook up the above function called NavEvents , which will handle our navigation events for us. It’s a pretty simple function, and really only a container for the router.events subscription.

this.router.events
.pipe(filter((e) => e instanceof NavigationEnd))
.subscribe((e: any) => {
this.showHideTabs(e);
});

All we’re doing is subscribing to the router.events Observable, filtering out the events so that we only get the NavigationEnd events, and then run another function with the event data. The reason we’re filtering out certain events is that Angular triggers a number of lifecycle events, whenever we navigate to a page. If we didn’t do this, the inner function this.showHideTabs(e) would be called numerous times with every page change, which isn’t ideal.

Now we should create our function showHideTabs(). This is what will handle whether the tabs should be shown, or hidden depending on the page we navigated to.

private showHidetabs(e: NavigationEnd) {
...
}

The e: NavigationEnd event will have the following information in it. This will be different depending on the page that you’re navigating to.

{
id: 13
url: "/tabs/groups/new-group?type=msg"
urlAfterRedirects: "/tabs/groups/new-group?type=msg"
}

First, we need to get the event URL in a format that we can use.

const urlArray = e.url.split('/');
// Result: urlArray: ["", "tabs", "groups", "new-group?type=group"]

Next, we’ll grab the last page URL in the path.

// Grab the last page url.
const pageUrl = urlArray[urlArray.length - 1];
// Result: new-group?type=group

Since we’re left with new-group?type=group, we need to remove the parameters, since we only want the page name. The parameters that I’m passing are shown after the ? character, as ?type=group. You should include this regardless, as it future proof’s your code.

// Remove the parameters from the URL.
const page = pageUrl.split('?')[0];
// Result: new-group

One of the last things we need to do in this function is check if we should hide this particular page or not.

// Check if we should hide or show tabs.
const shouldHide = this.hideTabBarPages.indexOf(page) > -1;
// Result: true

Lastly, we’ll need to actually hide the tabs if, or show them if they’ve been previously hidden.

shouldHide ? this.hideTabs() : this.showTabs()

The logic is mostly explained in the comments, but if you’re confused about the line shouldHide ? this.hideTabs() : this.showTabs() , this is called a ternary operator, which is a fancy way of writing an if / else statement.

Basically, it says that if ShouldHide is true, then run this.hideTabs() if shouldHide is false run this.showTabs() .


(recommended optional):

If you want to hide the tabs during the transition between pages, you can make a simple setTimeout, that will wait 300ms before running the above.

// Not ideal to set the timeout, but I haven't figured out a better method to wait until the page is in transition...
try {
setTimeout(() => shouldHide ? this.hideTabs() : this.showTabs(), 300);
} catch (err) {}

The try / catch statement is just for any weird errors that pop up. Sometimes the tabs aren’t initialized yet, even after we check if the platform is ready in the constructor. So this will prevent our application from throwing out an error, unnecessarily.


Create the hideTabs() & showTabs() functions.

Here I have set these methods as public, so we can access them outside of the service if necessary, but for this example, it does not affect anything. You can set them to private if you wish, but I recommend keeping them as public.

public hideTabs() {
const tabBar = document.getElementById('myTabBar');
if (tabBar.style.display !== 'none') tabBar.style.display = 'none';
}

public showTabs() {
const tabBar = document.getElementById('myTabBar');
if (tabBar.style.display !== 'flex') tabBar.style.display = 'flex';
}

I am also checking if the tab bar has a certain display value before changing it. flex for if we’re going to show the tabs and none if we’re going to hide them. This just prevents us from doing an unnecessary operation and possibly causing the tabs to flicker.


Lastly

You’ll need to add an ID to your tabBar element in your tabs.page.html. In the above example, the ID I included is called myTabBar, but you can name it whatever you want.

<ion-tab-bar #myTabBar id="myTabBar" slot="bottom">

That’s it!

There you have it. Save the changes we made, and test out, and you’ll see that your tabs automatically hide when you navigate to a page in the hideTabBarPages array.

Here is the full service, if you just want to copy-paste it, we’ve all done it before ;-)

import { Injectable } from '@angular/core';
import { filter } from 'rxjs/operators';
import { NavigationEnd, Router } from '@angular/router';
import { Platform } from '@ionic/angular';

@Injectable({
providedIn: 'root',
})
export class TabsService {

hideTabBarPages = [
'new-group',
];

constructor(private router: Router, private platform: Platform) {
this.platform.ready().then(() => {
console.log('Core service init');
this.navEvents();
});
}

public hideTabs() {
const tabBar = document.getElementById('myTabBar');
if (tabBar.style.display !== 'none') tabBar.style.display = 'none';
}

public showTabs() {
const tabBar = document.getElementById('myTabBar');
if (tabBar.style.display !== 'flex') tabBar.style.display = 'flex';
}

// A simple subscription that tells us what page we're currently navigating to.
private navEvents() {
this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe((e: any) => {
console.log(e);
this.showHideTabs(e);
});
}

private showHideTabs(e: any) {
// Result: e.url: "/tabs/groups/new-group?type=group"

// Split the URL up into an array.
const urlArray = e.url.split('/');
// Result: urlArray: ["", "tabs", "groups", "new-group?type=group"]

// Grab the last page url.
const pageUrl = urlArray[urlArray.length - 1];
// Result: new-group?type=group

const page = pageUrl.split('?')[0];
// Result: new-group

// Check if we should hide or show tabs.
const shouldHide = this.hideTabBarPages.indexOf(page) > -1;
// Result: true

// Not ideal to set the timeout, but I haven't figured out a better method to wait until the page is in transition...
try {
setTimeout(() => shouldHide ? this.hideTabs() : this.showTabs(), 300);
} catch (err) {
}
}
}

Questions?

You can find me on:
- GitHub: https://github.com/bengejd/
- Medium: https://medium.com/@JordanBenge

Who am I? I am a Software Developer who loves helping others and contributing to Open-Source. I’ve been working in the Ionic Framework since Ionic 1, and have tried to keep up to date on the latest and greatest when it comes to Hybrid Mobile App Development.

If you enjoyed this story, please click the 👏 button and share to help others find it! Feel free to leave a comment below if you need any help.