2

I am doing an intro screen for an app. This screen (3 slides) only appears when on new user. So the way I do it is:

  1. Default route is always going to this intro screen

  2. From the intro screen controller, check (cookie) to see if the user has seen this screen before. The depending on that, toggle $scope.showIntro. Then do $state.go() to next main app screen.

  3. In the intro view, use ng-if="showIntro". So if $scope.showIntro == false, the DOM is blank.

Here are the tricky parts:

  1. There is no way to check for whether the user has seen this screen or not because I am using $localStorage and it doesn't exist in app() when I create the route. That's why I have to do this check logic in the controller.

  2. I had to use ng-if because if I use ng-show, it's too slow and the user will see a flash of intro screens before being routed to main screen.

THE PROBLEM:

If I use ng-if on ion-view component, the title does not get refreshed once the DOM is injected into the view again. Why is that and how to fix this problem? Maybe some way to re-run digest?

Code http://codepen.io/hawkphil/pen/jPLqge

angular.module('ionicApp', ['ionic'])
    
.config(function($stateProvider, $urlRouterProvider) {
    
$stateProvider
      .state('intro', {
        url: '/',
        templateUrl: 'templates/intro.html',
        controller: 'IntroCtrl'
      })
      .state('main', {
        url: '/main',
        templateUrl: 'templates/main.html',
        controller: 'MainCtrl'
      });
    
      $urlRouterProvider.otherwise("/");
    
    })
    
    .controller('IntroCtrl', function($scope, $state, $ionicSlideBoxDelegate) {
      $scope.title = "<b>CUSTOM</b> TITLE";
      $scope.showIntro = true;
      
      // Called to navigate to the main app
      $scope.startApp = function() {
        $state.go('main');
      };
      $scope.next = function() {
        $ionicSlideBoxDelegate.next();
      };
      $scope.previous = function() {
        $ionicSlideBoxDelegate.previous();
      };
    
      // Called each time the slide changes
      $scope.slideChanged = function(index) {
        $scope.slideIndex = index;
      };
    })
    
    .controller('MainCtrl', function($scope, $state) {
      console.log('MainCtrl');
      
      $scope.toIntro = function(){
        $state.go('intro');
      }
    });
    body {
      cursor: url('http://ionicframework.com/img/finger.png'), auto;
    }
    .slider {
      height: 100%;
    }
    .slider-slide {
      padding-top: 80px;
      color: #000;
      background-color: #fff;
      text-align: center;
    
      font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; 
      font-weight: 300;
    }
    
    #logo {
      margin: 30px 0px;
    }
    
    #list {
      width: 170px;
      margin: 30px auto;
      font-size: 20px;
    }
    #list ol {
      margin-top: 30px;
    }
    #list ol li {
      text-align: left;
      list-style: decimal;
      margin: 10px 0px;
    }
    
    .button.ng-hide{
      display:none;
    }
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
        <title>Ionic App</title>
    
        <link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
        <script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
      </head>
    
      <body ng-app="ionicApp">
    
        <ion-nav-bar class="bar-light">
            <ion-nav-back-button>
            </ion-nav-back-button>
        </ion-nav-bar>
    
        <ion-nav-view></ion-nav-view>
    
    
        <script id="templates/intro.html" type="text/ng-template"> 
          <ion-view view-title="{{ title }}" ng-if="showIntro">
            <ion-nav-buttons side="left">
              <button class="button button-positive button-clear no-animation"
                      ng-click="startApp()" ng-show="!slideIndex">
                Skip Intro
              </button>
              <button class="button button-positive button-clear no-animation"
                      ng-click="previous()" ng-show="slideIndex > 0">
                Previous Slide
              </button>
            </ion-nav-buttons>
            <ion-nav-buttons side="right"> 
              <button class="button button-positive button-clear no-animation"
                      ng-click="next()" ng-show="slideIndex != 2">
                Next
              </button>
              <button class="button button-positive button-clear no-animation"
                      ng-click="startApp()" ng-show="slideIndex == 2">
                Start using MyApp
              </button>
            </ion-nav-buttons>
            <ion-slide-box on-slide-changed="slideChanged(index)">
              <ion-slide>
                <h3>Thank you for choosing the Awesome App!</h3>
                <div id="logo">
                  <img src="http://code.ionicframework.com/assets/img/app_icon.png">
                </div>
                <p>
                  We've worked super hard to make you happy.
                </p>
                <p>
                  But if you are angry, too bad.
                </p>
              </ion-slide>
              <ion-slide>
                <h3>Using Awesome</h3>
                
                <div id="list">
                  <h5>Just three steps:</h5>
                  <ol>
                    <li>Be awesome</li>
                    <li>Stay awesome</li>
                    <li>There is no step 3</li>
                  </ol>
                </div>
              </ion-slide>
              <ion-slide>
                <h3>Any questions?</h3>
                <p>
                  Too bad!
                </p>
              </ion-slide>
            </ion-slide-box>
          </ion-view>
        </script>
    
        <script id="templates/main.html" type="text/ng-template">
          <ion-view hide-back-button="true" view-title="Awesome">
            <ion-content class="padding">
              <h1>Main app here</h1>
              <button class="button" ng-click="toIntro()">Do Tutorial Again</button>
            </ion-content>
          </ion-view>
        </script>
        
      </body>
    </html>

EDIT 1

http://plnkr.co/edit/1FYr6GK6xMQjp9hA3N0M?p=preview

Below answer's example works only if the intro state has / as the url route. But if you put /intro or other url, the state kept iterating (screenshot).

function config($stateProvider, $urlRouterProvider) {
  $stateProvider

    .state('intro', {
      url: '/intro',
      templateUrl: 'intro.html',
      controller: 'IntroCtrl',
      data: {
        isIntro: true
      },          
    })

    .state('main', {
      url: '/main',
      templateUrl: 'main.html',
      controller: 'MainCtrl',
      data: {
        isIntro: false
      },            
    });

    $urlRouterProvider.otherwise("/intro");
}

enter image description here

HP.
  • 19,226
  • 53
  • 154
  • 253

1 Answers1

1

You can check your localstorage before the view starts using $stateChangeStart:

$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
    console.log("$rootScope => $stateChangeStart => " + toState.name || '');

    if (toState.data && angular.isObject(toState.data)) {
    if (toState.data.isIntro && $localStorage.visitedIntro)
    {
        event.preventDefault();
        $state.go("main");
    } 
    }
});  

What I have done is attach an attribute to every state so I know exactly which one is the intro (data.isIntro):

.state('intro', {
  url: '/',
  templateUrl: 'intro.html',
  controller: 'IntroCtrl',
  data: {
    isIntro: true
  },          
})

.state('main', {
  url: '/main',
  templateUrl: 'main.html',
  controller: 'MainCtrl',
  data: {
    isIntro: false
  },            
})

before the ruote change $stateChangeStart I check if the local storage has visitedIntro field set. If it is set I go to the main route.

In your intro controller you can set the value of localstorage property:

.controller('IntroCtrl', function($scope, $state, $ionicSlideBoxDelegate, $localStorage) {

  $scope.title = "<b>CUSTOM</b> TITLE";
  $scope.showIntro = true;

  // Called to navigate to the main app
  $scope.startApp = function() {
    $state.go('main');
  };
  $scope.next = function() {
    $ionicSlideBoxDelegate.next();
  };
  $scope.previous = function() {
    $ionicSlideBoxDelegate.previous();
  };

  // Called each time the slide changes
  $scope.slideChanged = function(index) {
    $scope.slideIndex = index;
    if (index === 2)
    {
      $localStorage.visitedIntro = true;
    }
  };

});

When the user slides and reaches the last slide I set $localStorage.visitedIntro = true

if (index === 2)
{
   $localStorage.visitedIntro = true;
}

If you want to see how it works check the complete example here.

Of course this is just an option. You can also use resolve of the ui-ruoter.

UPDATE:

The strange iteration is a known issue with ui-ruoter. You can check this question on SO.

You can change the otherwise this way and it works:

$urlRouterProvider.otherwise(function ($injector, $location) {
    var $state = $injector.get("$state");
    $state.go("intro");
}); 

I know it is not very elegant but al least it works.
Updated plunker.

Community
  • 1
  • 1
LeftyX
  • 35,328
  • 21
  • 132
  • 193
  • Thanks for the answer but my intro is like `app.intro` and has url `/intro/slides` which caused weird iteration. Do you know why? Updated question above. – HP. Jun 16 '15 at 01:16