Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save thealchemist2016/fb531e9a1016edc19b4f281612377559 to your computer and use it in GitHub Desktop.

Select an option

Save thealchemist2016/fb531e9a1016edc19b4f281612377559 to your computer and use it in GitHub Desktop.
Custom Ionic Tabs And Nested State Navigation
<html ng-app="ionicApp">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>Tabs Example</title>
<link href="https://code.ionicframework.com/1.0.0-beta.11/css/ionic.css" rel="stylesheet">
<script src="https://code.ionicframework.com/1.0.0-beta.11/js/ionic.bundle.js"></script>
</head>
<body>
<ion-nav-view></ion-nav-view>
<script id="templates/scaffold.html" type="text/ng-template">
<ion-nav-bar class="nav-title-slide-ios7 bar-positive">
<pr-nav-back-button class="button-icon"></pr-nav-back-button>
</ion-nav-bar>
<ion-nav-view animation="slide-left-right"></ion-nav-view>
</script>
<script id="templates/tabs.html" type="text/ng-template">
<ion-tabs class="tabs-icon-top tabs-positive">
<ion-tab title="Home" icon="ion-home" href="#/tab/home">
<ion-nav-view name="home-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="About" icon="ion-ios7-information" href="#/tab/about">
<ion-nav-view name="about-tab"></ion-nav-view>
</ion-tab>
<ion-tab title="Contact" icon="ion-ios7-world" ui-sref="tabs.contact">
<ion-nav-view name="contact-tab"></ion-nav-view>
</ion-tab>
</ion-tabs>
</script>
<script id="templates/home.html" type="text/ng-template">
<ion-view title="Home">
<ion-content class="padding">
<p>Example of Ionic tabs. Navigate to each tab, and
navigate to child views of each tab and notice how
each tab has its own navigation history.</p>
<p>
<a class="button icon icon-right ion-chevron-right" ui-sref="home.facts">Scientific Facts</a>
</p>
</ion-content>
</ion-view>
</script>
<script id="templates/facts.html" type="text/ng-template">
<ion-view title="Facts">
<ion-content class="padding">
<p>Here we are starting on a nested state (home.facts), but since we have no history of going to this nested state from home, the back button still appears, knowing there is a parent state to navigate to. If there is a view to go back to in Ionic's history, the back button will default to that.</p>
<p>All the code is wrapped up in an angular module that needs to be included as a dependency of the app module. After that, you just have to replace <code>ion-nav-back-button</code> tag with <code>pr-nav-back-button</code> tag.</p>
<p>
<a class="button icon ion-home" ui-sref="^"> Home</a>
<a class="button icon icon-right ion-chevron-right"
ui-sref=".facts2">More Facts</a>
</p>
</ion-content>
</ion-view>
</script>
<script id="templates/facts2.html" type="text/ng-template">
<ion-view title="Also Factual">
<ion-content class="padding">
<p>111,111,111 x 111,111,111 = 12,345,678,987,654,321</p>
<p>1 in every 4 Americans has appeared on T.V.</p>
<p>11% of the world is left-handed.</p>
<p>1 in 8 Americans has worked at a McDonalds restaurant.</p>
<p>$283,200 is the absolute highest amount of money you can win on Jeopardy.</p>
<p>101 Dalmatians, Peter Pan, Lady and the Tramp, and Mulan are the only Disney cartoons where both parents are present and don't die throughout the movie.</p>
<p>
<a class="button icon ion-home" ui-sref="^.^"> Home</a>
<a class="button icon ion-chevron-left" ui-sref="^">Scientific Facts</a>
</p>
</ion-content>
</ion-view>
</script>
<script id="templates/about.html" type="text/ng-template">
<ion-view title="About">
<ion-content class="padding">
<h3>Create hybrid mobile apps with the web technologies you love.</h3>
<p>Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components for building highly interactive apps.</p>
<p>Built with Sass and optimized for AngularJS.</p>
<p>
<a class="button icon icon-right ion-chevron-right" href="#/tab/navstack">Tabs Nav Stack</a>
</p>
</ion-content>
</ion-view>
</script>
<script id="templates/nav-stack.html" type="text/ng-template">
<ion-view title="Tab Nav Stack">
<ion-content class="padding">
<p><img src="https://ionicframework.com/img/diagrams/tabs-nav-stack.png" style="width:100%"></p>
</ion-content>
</ion-view>
</script>
<script id="templates/contact.html" type="text/ng-template">
<ion-view title="Contact">
<ion-content>
<div class="list">
<div class="item">
@IonicFramework
</div>
<div class="item">
@DriftyTeam
</div>
<a ui-sref="home.facts.facts2" class="item">
<h2>Facts 2</h2>
<p>This links to the <code>home.facts.facts2</code> sub-state. Notice, the tab will switch to home, but we will still be able to navigate up the state tree to <code>home.facts</code> and <code>home</code></p>
</a>
</div>
</ion-content>
</ion-view>
</script>
</body>
</html>
angular.module('ionicApp', ['ionic', 'stateBackButtonIonic'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('scaffold', {
abstract: true,
templateUrl: "templates/scaffold.html"
})
.state('tabs', {
url: "/tab",
parent: "scaffold",
abstract: true,
templateUrl: "templates/tabs.html"
})
.state('home', {
url: "/home",
parent: "tabs",
views: {
'home-tab': {
templateUrl: "templates/home.html",
controller: 'HomeTabCtrl'
}
}
})
.state('home.facts', {
url: "/facts",
views: {
'home-tab@tabs': {
controller: "FactsCtrl",
templateUrl: "templates/facts.html"
}
}
})
.state('home.facts.facts2', {
url: "/facts2",
views: {
'home-tab@tabs': {
templateUrl: "templates/facts2.html"
}
}
})
.state('tabs.about', {
url: "/about",
views: {
'about-tab': {
templateUrl: "templates/about.html"
}
}
})
.state('tabs.navstack', {
url: "/navstack",
views: {
'about-tab': {
templateUrl: "templates/nav-stack.html"
}
}
})
.state('tabs.contact', {
url: "/contact",
views: {
'contact-tab': {
templateUrl: "templates/contact.html"
}
}
});
$urlRouterProvider.otherwise("/tab/home/facts");
})
.controller('HomeTabCtrl', function($scope) {
console.log('HomeTabCtrl');
})
.controller('FactsCtrl', function($ionicNavBarDelegate) {
console.log('FactsCtrl');
});
angular.module('stateBackButtonIonic', [])
.config(function($provide) {
$provide.decorator('$ionicViewService', function($delegate, $rootScope) {
var registerFunction = $delegate.register;
$delegate.register = function() {
var viewHistory = $rootScope.$viewHistory,
rsp = registerFunction.apply(this, arguments);
if(viewHistory.forcedBack) {
rsp.navAction = 'moveBack';
rsp.navDirection = 'back';
viewHistory.forcedBack = null;
this.clearHistory();
}
return rsp;
};
return $delegate;
});
})
.directive('prNavBackButton', function($animate, $rootScope, $sanitize, $state, $ionicNavBarConfig, $ionicNgClick) {
var backIsShown = false,
viewHistory = $rootScope.$viewHistory;
function backViewIsPresent() {
return viewHistory.backView && viewHistory.backView.historyId === viewHistory.currentView.historyId;
}
function parentStateIsPresent() {
return !$state.$current.parent.abstract;
}
// Everytime history changes, check if the back button should be shown
// We ignore ionic's data saying whether it should be or not, favoring our own logic
$rootScope.$on('$viewHistory.historyChange', function() {
backIsShown = backViewIsPresent() || parentStateIsPresent();
});
return {
restrict: 'E',
require: '^ionNavBar',
compile: function(tElement) {
tElement.addClass('button back-button ng-hide');
var hasIconChild = !!(tElement.html() || '').match(/class=.*?ion-/);
return function($scope, $element, $attr, navBarCtrl) {
// Add a default back button icon based on the nav config, unless one is set
if (!hasIconChild && $element[0].className.indexOf('ion-') === -1) {
$element.addClass($ionicNavBarConfig.backButtonIcon);
}
//Default to ngClick going back, but don't override a custom one
if (!angular.isDefined($attr.ngClick)) {
$ionicNgClick($scope, $element, function() {
if (backViewIsPresent()) {
navBarCtrl.back();
} else if (parentStateIsPresent()) {
viewHistory.forcedBack = true;
$state.go('^');
} else {
return false;
}
});
}
//Make sure both that a backButton is allowed in the first place,
//and that it is shown by the current view.
$scope.$watch(function() {
if(angular.isDefined($attr.fromTitle)) {
$element[0].innerHTML = '<span class="back-button-title">' + $sanitize($scope.oldTitle) + '</span>';
}
return !!(backIsShown && $scope.backButtonShown);
}, ionic.animationFrameThrottle(function(show) {
if (show) { $animate.removeClass($element, 'ng-hide');
} else { $animate.addClass($element, 'ng-hide'); }
}));
};
}
};
})
.directive('ionTabs', function($rootScope, $state) {
var viewHistory = $rootScope.$viewHistory;
function getTabRootState(state) {
var isRootState;
if (state.parent.self.abstract === true) {
isRootState = state.self.name;
} else {
isRootState = false;
}
return isRootState || getTabRootState(state.parent);
}
return {
restrict: 'E',
require: 'ionTabs',
link: function(scope, element, attr, ctrl) {
var selectTab = ctrl.select;
ctrl.select = function(tab, shouldEmitEvent) {
var selectedTab = ctrl.selectedTab();
if (selectedTab && selectedTab.$historyId == tab.$historyId) {
if (shouldEmitEvent) {
viewHistory.forcedBack = true;
$state.go(getTabRootState($state.$current))
}
} else {
selectTab.apply(this, arguments);
}
};
}
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment