Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(navBar): animations, hide back button, no flicker #653

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
85 changes: 66 additions & 19 deletions js/ext/angular/src/directive/ionicViewState.js
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 @@ -40,30 +40,30 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
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>' +

'<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;
Expand Down Expand Up @@ -100,7 +100,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 +109,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
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