Navigator inside tabbar



  • Hello I am currently developing an app for Android and iOS. The app uses a tabbar and in each tabbar item there is a navigator.
    The tabbar initialization lets you specify properties/attributes like persistent and it also lets to listen to the “reactive” event. This event fires if the current active tab is tapped again.
    My desire behavior is to have the persistent property of the tab and to have the reactive behaviour in such a way that if I press the current active tab and fire the reactive event, I pop all the pages in the stack for that current navigator, another way to see it is to simply get the tabbar item reinstantiated just like it does when the persistent property is not defined. It would be nice to have it sort of like Facebook, where the “reactive” event simply takes you to the “home” for that tab.
    I was wondering if there was a way to access the tabbar refresh event or reinstantiate tabbar item event, or event better, if someone could provide an example of popping a single page to get back to the “home” tab page, again just like Facebook does.

    Code:

    //tabs.html sample
    <ons-tabbar var="tabbar">
           <ons-tab active="true" page="views/home/home.html" >
               <div class="tab">
                   <ons-icon icon="fa-home" class="tab-icon"></ons-icon>
                   <div class="tab-label">{{"HOME"|translate}}</div>
               </div>
           </ons-tab>
              ...
    //Home page for home tab, views/home/home.html
    <ons-navigator var="homeNavigator" >
        <ons-page ng-controller="HomeController" ng-device-backbutton="homeDeviceBackButton()">
            <ons-toolbar>
                <div class="center">{{"HEADER_MESSAGE_HOME"|translate}}</div>
                <div class="right">
                    <ons-toolbar-button ng-click="homeNavigator.pushPage('views/tabs/info-page-tabs.html',{param:home})">
                        <ons-icon icon="ion-ios-information-outline"></ons-icon>
                    </ons-toolbar-button>
                </div>
            </ons-toolbar>
             ...
    

    I have tried different methods such as popping all the pages except the first one, it looks aweful, or replacing/destroying pages in the navigator stack, this has problems because of the way I defined navigator in the “home” tab.

    Any suggestions?


  • Onsen UI

    @davidfherrerar Hello! If I understood it correctly, you just need to call navigator’s resetToPage on tabbar’s reactive event with the original page.

    You would need to give a name (ons-template or a file) to the initial page you load in the navigator and use navigator’s page attribute instead of including the page directly inside it.

    Does that work?



  • Hey Fran,

    Thank you for your time, you understood correctly and your solution solved my problem! Everything except for one thing, when you use resetToPage with animation set to ‘simpleslide’ or ‘slide’, the animation pushes the page instead of popping, so it looks like its pushing a page when in fact it should “pop” the page and reset to the “home” page. Is there a way to get the animation to do a popping animation instead of a pushing animation on the resetToPage method?



  • @davidfherrerar There are 4 options for animation as detailed here:

    https://onsen.io/v2/docs/js/navigation.html

    I am not certain what the default it (fade maybe), but you can try them out to find which one you like.



  • I am well aware of the four animations, I am currently using fade, the default is none, then there is slide, simpleslide, and lift, there is one missing though that might be important. It would complete all the normal animations, the pop page animation.

    I want the pop page animation, that one is not offered as any of the options. I was wondering if there was another way of getting it.



  • @davidfherrerar The default is actually slide according to the docs and the source code. Based on what you are saying, the slide is reversed in your project when you pop a page. If this is the case, a custom animation reversing the slide may resolve your issue. This was answered by @Fran-Diox here: http://stackoverflow.com/questions/29115915/how-to-change-slide-direction-for-resettopage

    Specifically, he wrote a custom animator located here: http://codepen.io/frankdiox/pen/azQMZE

    The animator is defined as such:

    ons.bootstrap();
    
      angular.module('onsen').factory('reversedSlide', function(NavigatorTransitionAnimator) {
    
        /**
         * Slide animator for navigator transition.
         */
        var reversedSlide = NavigatorTransitionAnimator.extend({
    
          /** Black mask */
          backgroundMask : angular.element(
            '<div style="z-index: 2; position: absolute; width: 100%;' +
            'height: 100%; background-color: black; opacity: 0;"></div>'
          ),
    
          timing: 'cubic-bezier(.1, .7, .4, 1)',
          duration: 0.3, 
          blackMaskOpacity: 0.4,
    
          init: function(options) {
            options = options || {};
    
            this.timing = options.timing || this.timing;
            this.duration = options.duration !== undefined ? options.duration : this.duration;
          },
    
          /**
           * @param {Object} enterPage
           * @param {Object} leavePage
           * @param {Function} callback
           */
          push: function(enterPage, leavePage, done) {
            var mask = this.backgroundMask.remove();
            enterPage.element[0].parentNode.insertBefore(mask[0], enterPage.element[0].nextSibling);
    
            animit.runAll(
    
              animit(mask[0])
                .queue({
                  opacity: this.blackMaskOpacity,
                  transform: 'translate3d(0, 0, 0)'
                })
                .queue({
                  opacity: 0
                }, {
                  duration: this.duration,
                  timing: this.timing
                })
                .resetStyle()
                .queue(function(done) {
                  mask.remove();
                  done();
                }),
    
              animit(enterPage.element[0])
                .queue({
                  css: {
                    transform: 'translate3D(-45%, 0px, 0px)',
                    opacity: 0.9
                  },
                  duration: 0
                })
                .queue({
                  css: {
                    transform: 'translate3D(0px, 0px, 0px)',
                    opacity: 1.0
                  },
                  duration: this.duration,
                  timing: this.timing
                })
                .resetStyle(),
    
              animit(leavePage.element[0])
                .queue({
                  css: {
                    transform: 'translate3D(0px, 0px, 0px)'
                  },
                  duration: 0
                })
                .queue({
                  css: {
                    transform: 'translate3D(100%, 0px, 0px)'
                  },
                  duration: this.duration,
                  timing: this.timing
                })
                .wait(0.2)
                .queue(function(finish) {
                  done();
                  finish();
                })
            );
          }
        });
    
        return reversedSlide;
      });
    
    
    angular.module('onsen').run(function(NavigatorView, reversedSlide) {
      NavigatorView.registerTransitionAnimator('foo', new reversedSlide())
    });
    

    And it is used via:

    <ons-button modifier="light" onclick="myNavigator.resetToPage('index.html', { animation : 'foo' })">
    

  • Onsen UI

    @munsterlander That was the way to extend animatinos in v1, but unfortunately it won’t work for v2.

    @davidfherrerar For Onsen UI 2 right now extending and modifying animators is not an easy task. We still need to make this easier. I’ve made an example with the simple slide animation here.
    Basically I made a new animator using simple slide’s pop method in the new push method and changing some minor stuff.

    The problem is that the core animators are written in ES6, but here you would need to write ES5 for the browser, and also right now we can only extend the very basic animator, not an already implemented one.

    If you really want a pop animation I think right now it would be easier to just delete the pages you don’t need from myNavigator.children HTMLCollection and perform a normal myNavigator.popPage() afterwards.

    Anyway, just for the record, this is the code for the custom animator:

    // CUSTOM ANIMATOR
    
    var customAnimator = function(options) {
      options = options || {};
    
      this.timing = options.timing || 'ease';
      this.duration = options.duration || 0.4;
      this.delay = options.delay || 0;
    
      var div = document.createElement('div');
      div.innerHTML = '<div style="position: absolute; width: 100%; height: 100%; z-index: 2; background-color: black; opacity: 0;"></div>';
      this.backgroundMask = div.firstChild;
      this.blackMaskOpacity = 0.4;
    };
    
    customAnimator.prototype = Object.create(OnsNavigatorElement.NavigatorTransitionAnimator.prototype);
    
    customAnimator.prototype.push = function(enterPage, leavePage, done) {
      this.backgroundMask.remove();
      enterPage.parentNode.insertBefore(this.backgroundMask, enterPage.nextSibling);
    
      animit.runAll(
    
        animit(this.backgroundMask)
          .saveStyle()
          .queue({
            opacity: this.blackMaskOpacity,
            transform: 'translate3d(0, 0, 0)'
          })
          .wait(this.delay)
          .queue({
            opacity: 0
          }, {
            duration: this.duration,
            timing: this.timing
          })
          .restoreStyle()
          .queue(done => {
            this.backgroundMask.remove();
            done();
          }),
    
        animit(enterPage)
          .saveStyle()
          .queue({
            css: {
              transform: 'translate3D(-45%, 0px, 0px)',
              opacity: 0.9
            },
            duration: 0
          })
          .wait(this.delay)
          .queue({
            css: {
              transform: 'translate3D(0px, 0px, 0px)',
              opacity: 1.0
            },
            duration: this.duration,
            timing: this.timing
          })
          .restoreStyle(),
    
        animit(leavePage)
          .queue({
            css: {
              transform: 'translate3D(0px, 0px, 0px)',
              zIndex: 10000,
              display: 'block'
            },
            duration: 0
          })
          .wait(this.delay)
          .queue({
            css: {
              transform: 'translate3D(100%, 0px, 0px)'
            },
            duration: this.duration,
            timing: this.timing
          })
          .wait(0.2)
          .queue(function(finish) {
            done();
            finish();
          })
      );
    };
    
    OnsNavigatorElement.registerAnimator('customAnimator', customAnimator);