Search code examples
javascriptangularjscordovaphonegap-buildonsen-ui

Onsen UI combined navigation (sliding-menu with tabbar and navigator)


I am upgrading my first PhoneGap application with a couple new functions that require to have separate pages.

I started implementing a sliding-menu, but I quickly bumped into problems.

The initial application had an ons-navigator that took care of all the navigation through the app.

I have 2 controllers: a LoginController and an AppController.

The structure of my index.html file looks like this:

<ons-sliding-menu main-page="navigator.html" menu-page="menu.html" side="left" max-slide-distance="250px" var="menu">
</ons-sliding-menu>

<ons-template id="menu.html">
  <ons-list>
    <ons-list-item modifier="tappable" onclick="menu.setMainPage('navigator.html', {closeMenu: true})">
      Home
    </ons-list-item>
    <ons-list-item modifier="tappable" onclick="menu.setMainPage('Page1.html', {closeMenu: true})">
      Page1
    </ons-list-item>
    <ons-list-item modifier="tappable" onclick="menu.setMainPage('Page2.html', {closeMenu: true})">
      Page2
    </ons-list-item>
  </ons-list>
</ons-template>

<ons-template id="navigator.html">
    <ons-navigator title="Navigator" var="myNavigator" page="main.html">

    </ons-navigator>
</ons-template>

This is the navigation part of the page.

Then I have the pages like this:

<ons-template id="login.html">  
    <ons-page ng-controller="LoginController" id="login">
        <ons-toolbar>
            <div class="center">Log In</div>
        </ons-toolbar>
        <div class="login-form">
            <input type="text" class="text-input--underbar" placeholder="Username" ng-model="email">
            <input type="password" class="text-input--underbar" placeholder="Password" ng-model="password">
            <br><br>
            <ons-button modifier="large" class="login-button" ng-click="checkLogin(email,password)" > Log In</ons-button>
            <a href="" ng-click="openRegisterPage()">Don't have an account? Register now!</a>
        </div>
    </ons-page>
</ons-template>

<ons-template id="main.html">
    <ons-page id="main" >
        <ons-tabbar>
            <ons-tab active="true" page="SP1.html">
                <div class="tab">
                    <ons-icon icon="ion-calendar" class="tab-icon"></ons-icon>
                    <div class="tab-label">SP1</div>
                </div>
            </ons-tab>

            <ons-tab page="SP2.html">
                <div class="tab">
                    <ons-icon icon="ion-checkmark" class="tab-icon"></ons-icon>
                    <div class="tab-label">SP2</div>
                </div>
            </ons-tab>

            <ons-tab page="settings.html">
                <div class="tab">
                    <ons-icon icon="ion-gear-a" class="tab-icon"></ons-icon>
                    <div class="tab-label">Settings</div>
                </div>
            </ons-tab>
        </ons-tabbar>
    </ons-page>
</ons-template>

In the pages mentioned in the main.html tabbar, I use the AppController, and in the login.html I use the LoginController.

When the application loads I have the following ons.ready function:

ons.ready(function() {
    var storage = window.localStorage;
    if(storage.getItem("username")!=null) {
        if(storage.getItem("password")!=null) {
                myNavigator.resetToPage('main.html');
        }
    }
    else
    {
        myNavigator.resetToPage('login.html');
    }
});

My first problem is, that the application initiates the AppController in the beginning, even though it shouldn't, so if there are no saved credentials in the cache, it still shows the main.html page for a second and then goes to the login.html. It does an unnecesary cycle and it's probably because of the navigation. I haven't found out why, i'm a little lost because of the 2 navigation systems.

The second problem is, that I cannot include ons-back buttons on the pages the sliding-menu has, because they appear for a second, and then they get overlapped by the header.

Any way, to clean up the navigational system, so the ons-sliding-menu and the ons-navigation work with eachother?

Also: Am I supposed to include an [ons-navigator var="myNavigator"] in all the pages that I tend to use the myNavigator.resetToPage('page.html') function on?

This is how Page1.html works (that is in the sliding-menu)

<ons-template id="Page1.html">
    <ons-navigator title="Navigator" var="myNavigator">
        <ons-page ng-controller="AppController" ng-init="ListLoad()" id="page1"> 

Page1.html has a list of items, that each are clickable and brings up a Details.html page.

The onclick event of the item (which has a simple myNavigator.resetToPage('details.html') function) didn't work until I added the to the page, but it seems to me that it's not right.

Any ways of clarifying the whole navigation in the onsen UI?

ALSO: Is there a way, to have the bottom tabbar visible on all the pages? So you could go to SP1.html or Settings.html from ANY page in the app?


Solution

  • Props for trying to use all navigation components in one app. Even though you may not be doing it the right way, but I guess that is why you asked this question.

    first problem is, that the application initiates the AppController

    It's doing that because you have set it to load main.html initially.

    <ons-template id="navigator.html">
        <ons-navigator title="Navigator" var="myNavigator" page="main.html"></ons-navigator>
    </ons-template>
    

    If you just remove the page attribute everything will be fine.

    The second problem is, that I cannot include ons-back buttons on the pages the sliding-menu has, because they appear for a second, and then they get overlapped by the header.

    TBH I'm not sure that I can grasp the situation very well. I am guessing that by header you mean an ons-toolbar element. If they are overlapping - why not just put them inside the ons-toolbar? Secondly this seems to be some sort of css issue, and for it you haven't provided neither the html nor css in order to reproduce, so it's hard to really help you with this amount of information.

    So the only thing I can suggest is doing right clickinspect element and check what the situation is. You haven't mentioned which version of Onsen UI you are using, but I guess there is a chance that the ons-backbuttons are hiding themselves because of some Onsen logic which tries to see if there is a previous page to go back to.

    Any way, to clean up the navigational system, so the ons-sliding-menu and the ons-navigation work with eachother?

    Well since you can see that managing the navigation using multiple components is hard, what I would suggest is try to limit yourself to use less components for the actual navigation (preferably one).

    The way which I see things you seem to be doing menu.setMainPage, so that means that when you do that you will lose all of the pages inside the navigator of your current page. However it seems that as a result of that you are trying to later put navigators in the other pages too.

    A much simpler solution for this case would be - let the main page of the sliding menu be only one page with a navigator. Afterwards instead of calling menu.setMainPage you can just call myNavigator.resetToPage and achieve the same result. Basically you use sliding menu only to get access to the sliding menu and then you use the navigator for all navigation purposes.

    Also: Am I supposed to include an [ons-navigator var="myNavigator"] in all the pages that I tend to use the myNavigator.resetToPage('page.html') function on?

    If you want to use myNavigator you should always have a navigator to access. That's why in my suggestion is to have it outside of the pages Page1, Page2 etc and then just them inside the navigator. (You always have navigator.html in the sliding-menu, but only change the the contents of the navigator to Page1, Page2 etc.)

    Any ways of clarifying the whole navigation in the onsen UI?

    Basically we have several navigation components. Each of them is rather simple providing one or two methods to load a page somewhere inside of it. The only exception is ons-navigator. So what is generally recommended is to use the navigator as the main component for navigation and have others like sliding-menu and tabbar only for the additional bonuses which they bring (the sliding side and the tabbar buttons).

    If you want to have many levels of navigation then that's up to you - as long as the navigation is structured as a tree and not a web (or spaghetti) it should still be fine. Usually a sheet and a pen are your best friends for deciding such navigations before the implementation.

    ALSO: Is there a way, to have the bottom tabbar visible on all the pages? So you could go to SP1.html or Settings.html from ANY page in the app?

    And just as things were becoming simpler we're adding one more complexity element to the table.

    Since you want to have the tabbar on ANY page of the app that means that EVERY page in the app should be inside the tabbar. I am guessing that you still want your sliding-menu, so I guess you should have something like ons-sliding-menu > ons-tabbar.

    In that case the solutions which come to my mind are:

    1. Since in the code which you have showed you are using only myNavigator.resetToPage and never pushPage then maybe you can just remove the navigator in general. So maybe you can have only ons-sliding-menu > ons-tabbar as your logic and then do all navigation through the tabbar. So this means using something like myTabbar.loadPage in all places where you want to load a page.

    2. If you want you could have a tree type navigation where you do something like the previous, but with the difference being that the pages which you want to use something like pushPage you have a navigator. So you will be choosing between setting the page of the tabbar and doing something with the navigator. In this solution though whenever you do an action with the tabbar you will lose the navigation stack.

    3. Again just don't use the tabbar for the navigation, but rather use the navigator instead. The way to do that is:

      1. Have ons-sliding-menu > ons-tabbar as your main html structure
      2. Initially load navigator.html inside the tabbar the same way which you're doing now with the sliding-menu.
      3. Don't provide active and page attributes to the tabs, but instead have something like ng-click="myNavigator.resetToPage('SP1.html')". I guess you can also modify some classes if you want them look activated.

    That's pretty much all I've got as suggestions.

    As conclusion I would advise you to try to follow the KISS principle.