Navigation Controller within Modal

Is there a way (or an example) of having a modal with a NavController in it? I would like to be able to push a few views on the stack from within the context of a modal.

For example, I am using modals for forms. I would like the ability to switch pages in the middle of working with a form. For example, have an that transitions the page to a new view for creating a separate entity or something. Does that make sense?

Thanks,
Dan

1 Like

Okay, I have established a way to do this:

I want to show a page called “AreaDetails”. I created an AreaDetailsWrapper Page that looks like this.

import {NavParams, Page, ViewController} from "ionic-angular";

import {Area} from "../../dao/area/Area";
import {AreaDetails} from "./AreaDetails";

@Page({
    template: `
        <ion-nav id="nav" [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>
    `
})
export class AreaDetailsWrapper{
    constructor(navParams:NavParams){
        var area = new Area();
        if ( navParams.data && navParams.data.area ){
            area = navParams.data.area.getPureObject();
        }
        if ( navParams.data && navParams.data.siteId ){
          area.siteId = navParams.data.siteId;
        }
        if ( navParams.data && navParams.data.parentId ){
          area.parentId = navParams.data.parentId;
        }
        this.area = area;
        this.rootPage = AreaDetails
    }
}

However, now I am unclear on how to get the data that I need, the “Area”, to the “AreaDetails” page. Is there an easy way to pass data to the root of a NavigationController?

I was able to do something like this:

In my AreaDetailsWrapper, which is really just an empty page that create a nav controller, I added an “AreaDetailsDelegate”.

import {NavParams, Page, ViewController} from "ionic-angular";

import {Area} from "../../dao/area/Area";
import {AreaDetails} from "./AreaDetails";
import {AreaDetailsDelegate} from "./AreaDetailsDelegate";

@Page({
    template: `
        <ion-nav id="nav" [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>
    `
})
export class AreaDetailsWrapper{
    constructor(areaDetailsDelegate:AreaDetailsDelegate, navParams:NavParams){
        this.areaDetailsDelegate = areaDetailsDelegate;

        var area = new Area();
        if ( navParams.data && navParams.data.area ){
            area = navParams.data.area.getPureObject();
        }
        if ( navParams.data && navParams.data.siteId ){
          area.siteId = navParams.data.siteId;
        }
        if ( navParams.data && navParams.data.parentId ){
          area.parentId = navParams.data.parentId;
        }
        this.area = area;
        this.areaDetailsDelegate.area = area;

        this.rootPage = AreaDetails;
    }
}

import {Injectable} from "angular2/core";

// this class is a total hack for now until I figure out to pass
// data across a navigation controller modal

@Injectable()
export class AreaDetailsDelegate{
    constructor(){
        this.area = null;
    }
}

Then, in my AreaDetails class, which is the first page in the navController within the modal, I can access the data I needed to share with the view via the delegate since it’s a singleton.

import {NavController, NavParams, Page, ViewController} from "ionic-angular";

import {Area} from "../../dao/area/Area";
import {AreaDao} from "../../dao/area/AreaDao";
import {AreaDetailsDelegate} from "./AreaDetailsDelegate";

import {IdentifierDetails} from "../identifier-details/IdentifierDetails";

@Page({
    template: `
    <ion-navbar *navbar primary>
        <ion-title>Area Details</ion-title>
        <ion-buttons end>
            <button (click)="dismiss()">
                <ion-icon name="close"></ion-icon>
            </button>
        </ion-buttons>
    </ion-navbar>
    <ion-content class="has-header">
        <form #areaForm="ngForm">
            <ion-item>
                <ion-label>Name <span danger>*</span></ion-label>
                <ion-input type="text" [(ngModel)]="area.name"></ion-input>
            </ion-item>
            <button ion-item (click)="identifierClicked()">
                <ion-label>Identifer <span danger>*</span></ion-label>
                <p>Test Test Test</p>
            </button>
            <ion-item>
                <ion-label>Permit Required? <span danger>*</span></ion-label>
                <ion-toggle danger [checked]="area.permitRequired"></ion-toggle>
            </ion-item>
            <ion-item>
                <ion-label stacked>Notes</ion-label>
                <ion-textarea [(ngModel)]="area.notes"></ion-textarea>
            </ion-item>
            <div padding>
                <button primary block
                [disabled]="!area.name || area.name.length === 0 || !area.identifier || area.identifier.length === 0"
                (click)="saveArea()">Save Area</button>
            </div>
        </form>
    </ion-content>
    `
})
export class AreaDetails{
    constructor(areaDao:AreaDao, areaDetailsDelegate:AreaDetailsDelegate, nav:NavController, viewController: ViewController){
        this.areaDao = areaDao;
        this.areaDetailsDelegate = areaDetailsDelegate;
        this.nav = nav;
        this.viewController = viewController;
    }

    onPageWillEnter() {
    	this.area = this.areaDetailsDelegate.area;
	}

    identifierClicked(){
        this.nav.push(IdentifierDetails, {test:"burritos"});
    }

    dismiss(){
        this.viewController.dismiss();
    }
}

I can even push another view onto the NavController as seen in the identifierClicked method above.

However, when I try to dismiss the modal via the ViewController.dismiss() method, I am given an error:

TypeError: Cannot read property 'direction' of null
    at NavController.remove (nav-controller.js:619)
    at ViewController.dismiss (view-controller.js:79)
    at AreaDetails.dismiss (AreaDetails.js:64)
    at AbstractChangeDetector.ChangeDetector_AreaDetails_1.handleEventInternal (viewFactory_AreaDetails:872)
    at AbstractChangeDetector.handleEvent (abstract_change_detector.js:58)
    at AppView.triggerEventHandlers (view.js:221)
    at eval (viewFactory_AreaDetails:1078)
    at dom_renderer.js:277
    at dom_events.js:28
    at Zone.run (zone-microtask.js:1217)

Anyone have any thoughts?

I could see this being a popular pattern for ionic-apps (multiple pages via nav in a modal). There’s gotta be a better way to do it then I’m doing it. Definitely a lot of code smells there :slight_smile:

1 Like

@danbucholtz_brady Did you figure this out?

Here’s an example of how I did it: https://github.com/yannbf/ionic3-components/tree/master/src/pages/modal-with-navigation
Thanks to @yannbf for the code review, and for starting the repo!

2 Likes

thank you for your @corymc project!
Your project worked swipe back?
I can’t swipe back nav to modal.

I haven’t tried adding swipe back. Remember this is inside a modal when on an iPad… so not sure if that is expected behavior. Feel free to submit a pull request!

Take a look at https://ionicframework.com/docs/api/navigation/NavController/#navigating-from-an-overlay-component

Anyway getRootNav() is deprecated so you have to use getActiveNav() (which will be also deprecated) or getActiveNavs()