feat(ionNavBar,ionHeaderBar): use declarative syntax

BREAKING CHANGE:

navBar is majorly different.  Manually write this when changelog is
released.  Add link to docs.
This commit is contained in:
Andy Joslin
2014-03-13 18:35:34 -06:00
parent f8b5c71778
commit 87a25a8bff
11 changed files with 980 additions and 719 deletions

View File

@@ -3,197 +3,6 @@
angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gesture', 'ngSanitize'])
/**
* @ngdoc directive
* @name ionNavBar
* @module ionic
* @restrict E
*
* @usage
* If have an {@link ionic.directive:ionNavView} directive, we can also create an
* <ion-nav-bar>, which will create a topbar that updates as the application state changes.
* We can also add some styles and set up animations:
*
* ```html
* <body ng-app="starter">
* <!-- The nav bar that will be updated as we navigate -->
* <ion-nav-bar animation="nav-title-slide-ios7"
* type="bar-positive"
* back-button-type="button-icon"
* back-button-icon="ion-arrow-left-c"></ion-nav-bar>
*
* <!-- where the initial view template will be rendered -->
* <ion-nav-view animation="slide-left-right"></ion-nav-view>
* </body>
* ```
*
* @param {string=} back-button-type The type of the back button's icon. Available: 'button-icon' or just 'button'.
* @param {string=} back-button-icon The icon to use for the back button. For example, 'ion-arrow-left-c'.
* @param {string=} back-button-label The label to use for the back button. For example, 'Back'.
* @param animation {string=} The animation used to transition between titles.
* @param type {string=} The className for the navbar. For example, 'bar-positive'.
* @param align {string=} Where to align the title of the navbar. Available: 'left', 'right', 'center'. Defaults to 'center'.
*/
.directive('ionNavBar', ['$ionicViewService', '$rootScope', '$animate', '$compile',
function( $ionicViewService, $rootScope, $animate, $compile) {
return {
restrict: 'E',
replace: true,
scope: {
animation: '@',
type: '@',
backType: '@backButtonType',
backLabel: '@backButtonLabel',
backIcon: '@backButtonIcon',
alignTitle: '@'
},
controller: function() {},
template:
'<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>' +
'<h1 ng-bind-html="title" class="title"></h1>' +
'<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) {
//defaults
$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({
el: $element[0],
alignTitle: $scope.alignTitle || 'center'
});
$scope.headerBarView = hb;
//Navbar events
$scope.$on('viewState.viewEnter', function(e, data) {
updateHeaderData(data);
});
$scope.$on('viewState.showNavBar', function(e, showNavBar) {
$scope.isInvisible = !showNavBar;
});
// All of these these are emitted from children of a sibling scope,
// so we listen on parent so we can catch them as they bubble up
var unregisterEventListeners = [
$scope.$parent.$on('$viewHistory.historyChange', function(e, data) {
$scope.backButtonEnabled = !!data.showBack;
}),
$scope.$parent.$on('viewState.leftButtonsChanged', function(e, data) {
$scope.leftButtons = data;
}),
$scope.$parent.$on('viewState.rightButtonsChanged', function(e, data) {
$scope.rightButtons = data;
}),
$scope.$parent.$on('viewState.showBackButton', function(e, data) {
$scope.backButtonEnabled = !!data;
}),
$scope.$parent.$on('viewState.titleUpdated', function(e, data) {
$scope.title = data && data.title || '';
})
];
$scope.$on('$destroy', function() {
for (var i=0; i<unregisterEventListeners.length; i++)
unregisterEventListeners[i]();
});
function updateHeaderData(data) {
if (angular.isDefined(data.hideBackButton)) {
$scope.backButtonEnabled = !!data.hideBackButton;
}
$scope.isReverse = data.navDirection == 'back';
$scope.animateEnabled = !!(data.navDirection && data.animate !== false);
$scope.leftButtons = data.leftButtons;
$scope.rightButtons = data.rightButtons;
$scope.oldTitle = $scope.title;
$scope.title = data && data.title || '';
// only change if they're different
if($scope.oldTitle !== $scope.title) {
if (!$scope.animateEnabled) {
//If no animation, we're done!
hb.align();
} 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 class="title" ng-bind-html="oldTitle"></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');
});
});
}
};
}
};
}])
/**
* @ngdoc directive
* @name ionView
@@ -219,10 +28,9 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
* </ion-nav-view>
* ```
*
* @param {expression=} left-buttons The leftButtons to display on the parent {@link ionic.directive:ionNavBar}.
* @param {expression=} right-buttons The rightButtons to display on the parent {@link ionic.directive:ionNavBar}.
* @param {string=} title The title to display on the parent {@link ionic.directive:ionNavBar}.
* @param {boolean=} hideBackButton Whether to hide the back button on the parent {@link ionic.directive:ionNavBar}.
* @param {boolean=} hideBackButton Whether to hide the back button on the parent
* {@link ionic.directive:ionNavBar}.
* @param {boolean=} hideNavBar Whether to hide the parent {@link ionic.directive:ionNavBar}.
*/
.directive('ionView', ['$ionicViewService', '$rootScope', '$animate',
@@ -230,46 +38,34 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
return {
restrict: 'EA',
priority: 1000,
require: '^?ionNavBar',
scope: {
leftButtons: '=',
rightButtons: '=',
title: '@',
hideBackButton: '@',
hideNavBar: '@',
hideBackButton: '&',
hideNavBar: '&',
},
compile: function(tElement, tAttrs, transclude) {
tElement.addClass('pane');
tElement[0].removeAttribute('title');
return function link($scope, $element, $attr) {
$rootScope.$broadcast('viewState.viewEnter', {
title: $scope.title,
navDirection: $scope.$navDirection || $scope.$parent.$navDirection
});
return function link($scope, $element, $attr, navBarCtrl) {
if (!navBarCtrl) {
return;
}
navBarCtrl.changeTitle($scope.title, $scope.$parent.$navDirection);
// Should we hide a back button when this tab is shown
$scope.hideBackButton = $scope.$eval($scope.hideBackButton);
if($scope.hideBackButton) {
$rootScope.$broadcast('viewState.showBackButton', false);
}
navBarCtrl.showBackButton(!$scope.hideBackButton());
// Should the nav bar be hidden for this view or not?
$rootScope.$broadcast('viewState.showNavBar', ($scope.hideNavBar !== 'true') );
// watch for changes in the left buttons
$scope.$watch('leftButtons', function(value) {
$scope.$emit('viewState.leftButtonsChanged', $scope.leftButtons);
});
$scope.$watch('rightButtons', function(val) {
$scope.$emit('viewState.rightButtonsChanged', $scope.rightButtons);
});
navBarCtrl.showBar(!$scope.hideNavBar());
// watch for changes in the title
$scope.$watch('title', function(val) {
$scope.$emit('viewState.titleUpdated', $scope);
$scope.$watch('title', function(val, oldVal) {
//Don't send in initial value, changeTitle does that
if (val !== oldVal) {
navBarCtrl.setTitle(val);
}
});
};
}
@@ -277,39 +73,6 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
}])
/**
* @private
*/
.directive('ionNavBackButton', ['$ionicViewService', '$rootScope',
function($ionicViewService, $rootScope) {
function goBack(e) {
var backView = $ionicViewService.getBackView();
backView && backView.go();
e.alreadyHandled = true;
return false;
}
return {
restrict: 'E',
scope: {
type: '=',
label: '=',
icon: '='
},
replace: true,
template:
'<button ng-click="goBack($event)" class="button back-button {{type}} ' +
'{{(icon && !label) ? \'icon \' + icon : \'\'}}">' +
'<i ng-if="icon && label" class="icon {{icon}}"></i> ' +
'{{label}}' +
'</button>',
link: function($scope) {
$scope.goBack = goBack;
}
};
}])
/**
* @ngdoc directive
* @name ionNavView