Skip to content

Commit

Permalink
fix(navBar): animations, hide back button, no flicker
Browse files Browse the repository at this point in the history
Closes #653
  • Loading branch information
ajoslin committed Feb 21, 2014
1 parent 53aeacc commit 465ea76
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 27 deletions.
94 changes: 73 additions & 21 deletions js/ext/angular/src/directive/ionicViewState.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
* Our Nav Bar directive which updates as the controller state changes.
*/
.directive('ionNavBar', ['$ionicViewService', '$rootScope', '$animate', '$compile',
function( $ionicViewService, $rootScope, $animate, $compile) {
function( $ionicViewService, $rootScope, $animate, $compile) {

return {
restrict: 'E',
Expand All @@ -38,36 +38,41 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
},
controller: function() {},
template:
'<header class="bar bar-header nav-bar {{type}} {{isReverse ? \'reverse\' : \'\'}} ' +
'{{isInvisible ? \'invisible\' : \'\'}} {{animateEnabled ? animation : \'\'}}">' +
'<ion-nav-back-button ng-if="backButtonEnabled && (backType || backLabel || backIcon)" ' +
'type="backType" label="backLabel" icon="backIcon" class="invisible" ion-async-visible>' +
'<header class="bar bar-header nav-bar{{navBarClass()}}">' +
'<ion-nav-back-button ng-if="(backType || backLabel || backIcon)" ' +
'type="backType" label="backLabel" icon="backIcon" class="hide" ' +
'ng-class="{hide: !backButtonEnabled}">' +
'</ion-nav-back-button>' +
'<div class="buttons left-buttons"> ' +
'<button ng-click="button.tap($event)" ng-repeat="button in leftButtons" ' +
'class="button no-animation {{button.type}}" ng-bind-html="button.content">' +
'</button>' +
'</div>' +

//ng-repeat makes it easy to add new / remove old and have proper enter/leave anims
'<h1 ng-repeat="title in titles" ng-bind-html="title" class="title invisible" ion-async-visible ion-nav-bar-title></h1>' +
'<h1 ng-bind-html="title" class="title"></h1>' +

'<div class="buttons right-buttons" ng-if="rightButtons.length"> ' +
'<button ng-click="button.tap($event)" ng-repeat="button in rightButtons" '+
'class="button no-animation {{button.type}}" ng-bind-html="button.content">' +
'<div class="buttons right-buttons"> ' +
'<button ng-click="button.tap($event)" ng-repeat="button in rightButtons" '+
'class="button no-animation {{button.type}}" ng-bind-html="button.content">' +
'</button>' +
'</div>' +
'</header>',
compile: function(tElement, tAttrs) {

return function link($scope, $element, $attr) {
$scope.titles = [];
//defaults
$scope.backButtonEnabled = true;
$scope.backButtonEnabled = false;
$scope.animateEnabled = true;
$scope.isReverse = false;
$scope.isInvisible = true;

$scope.navBarClass = function() {
return ($scope.type ? ' ' + $scope.type : '') +
($scope.isReverse ? ' reverse' : '') +
($scope.isInvisible ? ' invisible' : '') +
(!$scope.animationDisabled && $scope.animation ? ' ' + $scope.animation : '');
};

// Initialize our header bar view which will handle
// resizing and aligning our title labels
var hb = new ionic.views.HeaderBar({
Expand Down Expand Up @@ -100,7 +105,7 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
$scope.backButtonEnabled = !!data;
}),
$scope.$parent.$on('viewState.titleUpdated', function(e, data) {
$scope.titles[$scope.titles.length - 1] = data && data.title || '';
$scope.title = data && data.title || '';
})
];
$scope.$on('$destroy', function() {
Expand All @@ -109,19 +114,66 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
});

function updateHeaderData(data) {
var newTitle = data && data.title || '';

$scope.isReverse = data.navDirection == 'back';

if (data.hideBackButton) {
$scope.backButtonEnabled = false;
if (angular.isDefined(data.hideBackButton)) {
$scope.backButtonEnabled = !!data.hideBackButton;
}

$scope.isReverse = data.navDirection == 'back';
$scope.animateEnabled = !!(data.navDirection && data.animate !== false);
$scope.titles.length = 0;
$scope.titles.push(newTitle);

$scope.leftButtons = data.leftButtons;
$scope.rightButtons = data.rightButtons;
$scope.oldTitle = $scope.title;
$scope.title = data && data.title || '';

//If no animation, we're done!
if (!$scope.animateEnabled) {
hb.align();
return;
} else {
animateTitles();
}
}

function animateTitles() {
var oldTitleEl, newTitleEl, currentTitles;

//If we have any title right now (or more than one, they could be transitioning on switch),
//replace the first one with an oldTitle element
currentTitles = $element[0].querySelectorAll('.title');
if (currentTitles.length) {
oldTitleEl = $compile('<h1 ng-bind-html="oldTitle" class="title"></h1>')($scope);
angular.element(currentTitles[0]).replaceWith(oldTitleEl);
}
//Compile new title
newTitleEl = $compile('<h1 class="title invisible" ng-bind-html="title"></h1>')($scope);

//Animate in one frame
ionic.requestAnimationFrame(function() {

oldTitleEl && $animate.leave(angular.element(oldTitleEl));

var insert = oldTitleEl && angular.element(oldTitleEl) || null;
$animate.enter(newTitleEl, $element, insert, function() {
hb.align();
});

//Cleanup any old titles leftover (besides the one we already did replaceWith on)
angular.forEach(currentTitles, function(el) {
if (el && el.parentNode) {
//Use .remove() to cleanup things like .data()
angular.element(el).remove();
}
});

//$apply so bindings fire
$scope.$digest();

//Stop flicker of new title on ios7
ionic.requestAnimationFrame(function() {
newTitleEl[0].classList.remove('invisible');
});
});
}
};
}
Expand Down
12 changes: 6 additions & 6 deletions js/ext/angular/test/directive/ionicView.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,23 @@ describe('Ionic View', function() {
function backButton() {
return angular.element(element[0].querySelector('.back-button'));
};
expect(backButton().length).toEqual(1);
expect(backButton().hasClass('hide')).toEqual(true);

scope.$broadcast('viewState.showBackButton', false);
scope.$apply();
expect(backButton().length).toEqual(0);
expect(backButton().hasClass('hide')).toEqual(true);

scope.$broadcast('viewState.showBackButton', true);
scope.$apply();
expect(backButton().length).toEqual(1);
expect(backButton().hasClass('hide')).toEqual(false);

scope.$broadcast('$viewHistory.historyChange', { showBack: false });
scope.$apply();
expect(backButton().length).toEqual(0);
expect(backButton().hasClass('hide')).toEqual(true);

scope.$broadcast('$viewHistory.historyChange', { showBack: true });
scope.$apply();
expect(backButton().length).toEqual(1);
expect(backButton().hasClass('hide')).toEqual(false);
});

it('should show/hide navBar', function() {
Expand Down Expand Up @@ -113,7 +113,7 @@ describe('Ionic View', function() {
scope.$digest();
var navBar = element.find('header');
var title = navBar.find('h1');
expect(title.text().trim()).toEqual('Title');
expect(element.find('header').find('h1').text().trim()).toEqual('Title');

scope.viewTitle = 'New Title';
scope.$digest();
Expand Down

0 comments on commit 465ea76

Please sign in to comment.