How to dynamically show an ion-menu in Ionic 4

This article teaches you how to dynamically show & hide an ion-menu, also known as a hamburger menu, slide-menu, side-menu, etc…

Jordan Benge
8 min readMay 13, 2019
Photo by Blake Richard Verdoorn on Unsplash

This article builds upon a previous one I wrote about Hiding & Showing Tabs on certain pages, within an Ionic 4 app. Instead of duplicating and explaining all of the code from that article in this one, I’m just going to explain how we can apply those same concepts that we worked on to hide & show the ion-tabs, to the ion-menu.

I recommend you check out that article above, before proceeding, as it’s really useful, and we’ll be using a lot of the same code.

Here is a link to the Ionic-documentation for the ion-menu if you want more information on the component.

The biggest issue I originally found with ion-menu (sliding menus), is that they are accessible app-wide. This isn’t ideal since I don’t always want them to show, and in my particular case, I only wanted a menu on one particular page, which was a chat page between two users. So I decided to write this article to help all of you who probably have similar use-cases.

Generate the side-menu component

First things first. Generate a new component called side-menu by usingionic g component components/side-menu This is going to house our ion-menu, and I will be referring to it instead from here on out.

After it’s finished running, add it to your app.module.ts or shared.module.ts if you have one. I prefer having a shared module but to each their own.

@NgModule({
declarations: [
SideMenuComponent,
...
],
...
exports: [
SideMenuComponent,
...
],
...
})
export class AppModule {
}

Generate the menu service:

We’ll be using a service to handle passing our menu links to the side-menu component. This is so the menu links can be dynamic and unique to the page that is accessing them.

ionic g service services/menu

From there, you can copy and paste the code below. You may notice that I have included a type of <SideMenu> on my replaySubject, which you can change to <any> for simplicities sake.

I have also set the replaySubject to have a buffer size of 1. This is because we just want a unique observable for the menu, and don’t need any caching capabilities.

import { Injectable } from '@angular/core';
import { MenuController } from '@ionic/angular';
import { ReplaySubject } from 'rxjs';
import { SideMenu } from '../../classes/side-menu';

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

public details: ReplaySubject<SideMenu> = new ReplaySubject<SideMenu>(1);

constructor(public menu: MenuController) { }

public toggle() {
this.menu.toggle('slidingMenu');
}
}

details is what we’ll be using to push our menu links to as we navigate to pages that require the menu to be shown, and thetoggle function is to manually show and hide the menu, in addition to being able to swipe from the direction of your choice. You’ll need to import MenuService into your app.module.ts or shared.module.ts providers array.

side-menu.component.ts

Inside the side-menu.component.ts that ionic generated for us, we’ll need to add a few things. First off, we’ll need to import the MenuService and the CoreService which is just a renamed version of the TabsService from the previous article, so that we can use them as necessary.

constructor(public menu: MenuService, public core: CoreService) {}

At the top of your component, right below the imports paste the

export interface MenuLink {
title: string;
linkHref: string;
}

This is a simple interface for our menu links. It includes a title, which is the text we’ll be displaying, and a linkHref which is where the menu-link takes us when it’s clicked.

Next, create a function called navToPage() which will allow us to navigate to pages from the sideMenu component.

public navToPage(link: MenuLink) {
console.log('Navigating to: ', link.linkHref);
this.core.navToPage(link.linkHref);
}

You can replace link: MenuLink with link: any, if you want to. But typing always helps keep projects nice and tight.

side-menu.component.html

Inside your side-menu HTML file, copy and paste the following:

<ion-menu [style.display]="'none'" side="end" contentId="mainContent" menuId="slidingMenu" id="slidingMenu">
<ion-header>
<ion-toolbar>
<ion-title>{{(menu?.details | async)?.chat?.name}}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>

<ion-list>
<ion-item *ngFor="let link of (menu?.details | async)?.links" detail (click)="navToPage(link)">{{link?.title}}</ion-item>
</ion-list>
</ion-content>
</ion-menu>

To explain a little bit of what is going on here:

<ion-menu [style.display]="'none'" side="end" contentId="mainContent" menuId="slidingMenu" id="slidingMenu">
  1. We set the menu to have an initial display of none through the use of [style.display]=”’none’. This is because in my app at least, I don’t start out on a page that should show the ion-menu at first.
  2. The side is set as end which means the menu will be accessible from the right side of the app. I made it this way because typically, swiping from the left side of the app means you are trying to go back, from a user perspective. Users have been trained to expect certain behavior, so I try not to mess with their expectations.
  3. contentId, menuId and id are one of the most important parts of the file as it’ll allow us to connect the ion-menu to our app. We’ll talk about them more, later.
<ion-title>{{(menu?.details | async)?.chat?.name}}</ion-title>

This is just setting a title for our menu. It’s completely optional and can be removed if you don’t want to have a title for your use-case.

<ion-list>
<ion-item *ngFor="let link of (menu?.details | async)?.links" detail (click)="navToPage(link)">{{link?.title}}</ion-item>
</ion-list>

The ion-item is what we’re using to display our links. There is a regular Angular *ngFor="let link of (menu?.details | async)?.links set to repeat however many links we want to show. The reason it’s in parenthesis is so we can allow Angular handle subscribing to the details replaySubject from our menuService asynchronously, and not be thrown an error when the links aren’t there initially, through the use of the ?.links.

I have also included the detail attribute on the ion-item because I think it adds to the UX, making the user aware that they can click the text to do something (in our case navigate to a page).

We have not yet implemented any actual links yet. This is because it’s going to be dependent on the page that we are currently in. As I mentioned earlier, I did it this way, to make the menu more dynamic so I could show different links and titles for my menu component.

So in any page that will show the ion-menu, we’ll need to import our menuService in the page’s constructor.

constructor(private menu: MenuService, ...) {
...
}

In the ngOnInit or the ionViewWillEnter function for that page, you’ll need to create the links that you want to navigate to. You can use the menuLinks interface that we created earlier, but it’s completely optional. It’ll end up looking something like the following:

ngOnInit() {
const sideLinks: MenuLink[] = [
{
title: 'Link one title!',
linkHref: 'tabs/home',
},
{
title: 'Link two title',
linkHref: 'tabs/home',
},
];
}

Where the title is whatever text you want to be displayed on the menu-link and the linkHref is the page route that you want to navigate to from that menu. As you can see, I have the linkHref for both of the menuLinks set to the tabs/home, just for simplicities sake, but you can change it to something else if you want.

Now we’re just going to update our ngOnInit or ionViewWillEnter function to update the details replaySubject from our menuService.

ngOnInit() {
const sideLinks: MenuLink[] = [
{
title: 'Link one title!',
linkHref: 'tabs/home',
},
{
title: 'Link two title',
linkHref: 'tabs/home',
},
];
this.menu.details.next(sideLinks);
}

App.component.html

Next, we have to actually add the side-menu component to our app. Don’t worry, we’re almost done!

You should start off with something looking like this in your app.component.html:

<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>

Which we will change to look like:

<ion-app>
<side-menu></side-menu>
<ion-router-outlet id="mainContent" main></ion-router-outlet>
</ion-app>

It’s important that the id="mainContent" is the same value as the contentId we set in our side-menu.component.html file.

If you listened to me, and have followed the previous article, then you’ll have a good base to alter to fit our needs. If not, you’re probably going to be lost from here on out.

tabs.service.ts (renamed to core.service.ts):

I renamed my service from tabsService to coreService since it no longer pertains only to handling tabs functionality but creating a new service solely for the ion-menu would not be a very DRY solution, since it shares most of the same logic.

In core.service.ts add the variable showSideMenuPages. This is just a string array, which contains the pages that we want to show the sideMenuComponent on. This is exactly like the hideTabBarPages array from the previous article:

showSideMenuPages: string[] = [
'chats',
];

In my case, I have a page called Chats.

Next in the showHideTabs function, add the following variable below shouldHideTabs:

const shouldShowSideMenu = this.showSideMenuPages.indexOf(page) > -1;

Then use that variable with:

this.showHideMenu(shouldShowSideMenu);

Lastly, we’ll need to hook up the function showHideMenu which we just called. It should look like this:

public showHideMenu(show: boolean = false) {
try {
const menu = document.getElementById('slidingMenu');
if (show && menu.style.display != 'initial') menu.style.display = 'initial';
else if (!show && menu.style.display != 'none') menu.style.display = 'none';
} catch (err) {}
}

Where the elementId slidingMenu is the same as your side-menu component’s menuId="slidingMenu" and id="slidingMenu"

If you save your changes and followed the article to a T, then you should be good to go. Navigate to a page that the menu should not show on, and swipe from the right side of the screen, and you’ll see nothing happens.

Now if you navigate to a page that should show the menu (in our showSideMenuPages array) and swipe from the right, you’ll see that the menu should appear like magic! You can tap to the left of the menu to close it or hook up the (click)="toggleMenu" function from the menuService to manually close it if you tap or click an element.

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.

<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>

We’re going to add our ion-menu here (since it has to be in the root for this to work). So replace what you have above, with the code below.

<ion-app>
<ion-menu side="end" contentId="mainContent" menuId="slidingMenu">
<ion-header>
<ion-toolbar>
<ion-title>
Sliding Menu Title
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>

<ion-router-outlet id="mainContent" main></ion-router-outlet>
</ion-app>

Perfect! If you reload your app, you should see that you now have a menu on the right side of your app, if you drag your cursor (or finger) from the right side of your screen over a little bit to the left.

Now we have to add a few more things for this to work the way we want it to (so that it only shows up on certain pages).

Change your ion-menu tag to include the following:

<ion-menu [style.display]="'none'" side="end" contentId="mainContent" menuId="slidingMenu" id="slidingMenu">

If you reload, you’ll notice that the menu is no longer shown when you swipe from the right side of the screen. That’s good. That’s what we want to happen.

If you listened to what I said earlier and completed the steps listed in my other article, then you’re good to go. If not, you kinda have to go back and do that before proceeding.

--

--

Jordan Benge

My name is Jordan Benge, I’m a freelance developer, who sometimes likes to write helpful articles on Medium for people who want to do things but don’t know how.