3

My problem:

My application contains a menu which is swipe enabled. On login screen if I swipe I can see the menu which is not right. I want to disable menu swipe for pages that doesn't contains menu icon, like login, inner details pages containing back button, etc.

Solution found:

I am able to do that by following the SO link - https://stackoverflow.com/a/38654644/2193918

I created a base class and injected a menu object in it. Override ionViewDidEnter() and ionViewDidLeave() In sub class, I inherited the base class. I have to write super() call in derived class constructor and pass the menu object back to the super class.

Is their any other way of doing it as with this way I will have to do this in every single page.

Please check the snippet of code as below:

Base class

import { MenuController } from "ionic-angular";

export class BaseComponent {
    constructor(public menu: MenuController) {

    }

    ionViewDidEnter() {
        this.menu.swipeEnable(false);
    }

    ionViewDidLeave() {
        this.menu.swipeEnable(true);
    }
}

Derived class

import { Component } from '@angular/core';
import { NavController, LoadingController, Events, MenuController } from 'ionic-angular';


@Component({
        selector: 'login',
        templateUrl: 'login.component.html'
    })

export class login extends BaseComponent {
    constructor(public menu: MenuController) {
        super(menu)
    }
}
sebaferreras
  • 44,206
  • 11
  • 116
  • 134
Jassi
  • 537
  • 8
  • 21

2 Answers2

11

Even though what @trichetriche says is true, there's a better way to handle this in Ionic! The answer is Custom decorators.

Github repo with working demo.


First, you'll need to go to the app.module.ts file and replace this line

export class AppModule { }

by this:

export class AppModule {

    static injector: Injector;

    constructor(injector: Injector) {    
        // Make the injector to be available in the entire module
        AppModule.injector = injector;    
    }
}

Doing that will help us to inject the instance of the MenuController in our custom decorator.

We're now able to write our custom decorator. I've created a folder named CustomDecorators and a file inside, disable-side-menu.decorator.ts with this content (I think the code is pretty self-explanatory):

// Angular
import { AppModule } from "../path/to.../app.module";

// Ionic
import { MenuController } from "ionic-angular";

export function DisableSideMenu() {

    return function (constructor) {
        const originalDidEnter = constructor.prototype.ionViewDidEnter;
        const originalWillLeave = constructor.prototype.ionViewWillLeave;            

        constructor.prototype.ionViewDidEnter = function () {

            // Get the MenuController instance
            const menuCtrl = AppModule.injector.get(MenuController);

            // Disable the side menu when entering in the page
            menuCtrl.enable(false);

            console.log('Disabling side menu...');

            // Call the ionViewDidEnter event defined in the page
            originalDidEnter && typeof originalDidEnter === 'function' && originalDidEnter.apply(this, arguments);
        };

        constructor.prototype.ionViewWillLeave = function () {

            // Get the MenuController instance
            const menuCtrl = AppModule.injector.get(MenuController);

            // Enable the side menu when leaving the page
            menuCtrl.enable(true);

            console.log('Enabling side menu...');

            // Call the ionViewWillLeave event defined in the page
            originalWillLeave && typeof originalWillLeave === 'function' && originalWillLeave.apply(this, arguments);
        };
    }

}

That's it! If you want to disable the side menu in a particular page, you'd need to add our custom decorator like this:

import { DisableSideMenu } from '../the/path/to../custom-decorators/disable-side-menu.decorator';

@DisableSideMenu()
@Component({
    selector: 'page-demo-page',
    templateUrl: 'demo-page.html'
})
...

So you don't need to extend any BaseClass nor inject anything else, making this extremely easy to be reused.

Jassi
  • 537
  • 8
  • 21
sebaferreras
  • 44,206
  • 11
  • 116
  • 134
  • I implemented the code above as given. But while running ionic serve, I am getting a Runtime error on browser. __WEBPACK_IMPORTED_MODULE_0__app_app_module__.a is undefined – Jassi Dec 22 '17 at 09:03
  • Did you added this import in the `app.module.ts`?: `import { Injector } from '@angular/core';`. Please let me know if the error persists and I'll create a demo app in Github so you can take a look at the code. – sebaferreras Dec 22 '17 at 09:10
  • Yes, I have added the import in App Module file – Jassi Dec 22 '17 at 09:19
  • I'm sorry for that, I've fixed it by moving the `const menuCtrl = AppModule.injector.get(MenuController);` line inside of the function. Could you please try now?. In a few minutes, I'll add a link to a Github demo with the decorator working properly. – sebaferreras Dec 22 '17 at 09:43
  • 1
    Yes, it is working now. Thanks!. I think this is the much better solution. – Jassi Dec 22 '17 at 09:59
  • Glad to hear that :) @Jassi – sebaferreras Dec 22 '17 at 10:02
  • @sebaferreras thank you, I learnt something new reading your answer :O –  Dec 22 '17 at 16:32
  • 1
    Just a question if you mind : are decorators just functions ? How do one declare a function as a decorator ? –  Dec 22 '17 at 16:35
  • Glad to hear that @trichetriche :) Yes, they're just functions. You can find some more information **[here](https://netbasal.com/inspiration-for-custom-decorators-in-angular-95aeb87f072c)**. – sebaferreras Dec 23 '17 at 12:44
  • @Jassi I've seen the proposed edit to use `ionViewWillLeave` instead of `ionViewDidLeave`. Is that because of an error/bug when using `ionViewDidLeave`? – sebaferreras Dec 27 '17 at 12:53
  • 1
    Yes its a bug, ionViewDidLeave is called before ionViewDidEnter, so the issue is for first page the menu is disabled which works fine, but when you go to inner page it gets enabled again as the ionViewDidEnter of second page called first and then ionViewDidLeave of Page1 which again enables it. – Jassi Dec 27 '17 at 13:10
  • @sebaferreras +1 for this and something new I learnt. One more thing is like when the user logins I have `this.navCtrl.push(ActionPage)` Now a back button appears I tried `this.navCtrl.setRoot(ActionPage)` but this time nothing appears on the Action page. Please suggest. – Zaker Feb 22 '18 at 16:35
  • Glad to hear that it helped @User :). May I ask what do you mean by _but this time nothing appears on the Action page._? – sebaferreras Feb 22 '18 at 16:43
  • 1
    @sebaferreras I mean when I use `this.navCtrl.setRoot(ActionPage)` then I cannot see either Menu nor back button. I loose the buttons. How can I show Menu button when user got logged in. – Zaker Feb 22 '18 at 16:49
  • Oh, I see. Please take a look at **[this answer](https://stackoverflow.com/questions/37905597/use-nav-push-with-side-menu-in-ionic-2/37905896#37905896)**. The key to show the menu toggle is to add the `persistent="true"` attribute, like this: `...` @User – sebaferreras Feb 22 '18 at 18:14
1

Short answer : no. Inheriting a class implies that you have to call super in your constructor. If you don't want to do that, then you need to find another way of doing this.

  • After lot of search on Google, I posted the question. If you know any alternate solution then please guide or refer a link. I will be very thankful. – Jassi Dec 22 '17 at 08:21
  • Actually that is the best solution ! I'm not saying you're doing wrong, I'm saying if you want to stop writing super, then you have to find another solution. But after all, that's just one word, and it allow you to see in the blink of an eye that you have some logic through it. I would keep doing that if I were you ! –  Dec 22 '17 at 08:25