diff --git a/js/ext/angular/src/directive/ionicBar.js b/js/ext/angular/src/directive/ionicBar.js
index 5696fb4b1e..2f92b495e5 100644
--- a/js/ext/angular/src/directive/ionicBar.js
+++ b/js/ext/angular/src/directive/ionicBar.js
@@ -112,25 +112,47 @@ angular.module('ionic.ui.header', ['ngAnimate', 'ngSanitize'])
.directive('ionFooterBar', barDirective(false));
function barDirective(isHeader) {
- var BAR_TEMPLATE = isHeader ?
- '' :
- '';
- var BAR_MODEL_DEFAULT = isHeader ?
- '$ionicHeaderBarController' :
- '$ionicFooterBarController';
return ['$parse', function($parse) {
return {
restrict: 'E',
- replace: true,
- transclude: true,
- template: BAR_TEMPLATE,
- link: function($scope, $element, $attr) {
- var hb = new ionic.views.HeaderBar({
- el: $element[0],
- alignTitle: $attr.alignTitle || 'center'
- });
+ compile: function($element, $attr) {
+ $element.addClass(isHeader ? 'bar bar-header' : 'bar bar-footer');
+ return { pre: prelink };
+ function prelink($scope, $element, $attr) {
+ var hb = new ionic.views.HeaderBar({
+ el: $element[0],
+ alignTitle: $attr.alignTitle || 'center'
+ });
- $parse($attr.controllerBind || BAR_MODEL_DEFAULT).assign($scope, hb);
+ $parse($attr.controllerBind ||
+ (isHeader ? '$ionicHeaderBarController' : '$ionicFooterBarController')
+ ).assign($scope, hb);
+
+ var el = $element[0];
+
+ if (isHeader) {
+ $scope.$watch(function() { return el.className; }, function(value) {
+ var isSubheader = value.indexOf('bar-subheader') !== -1;
+ $scope.$parent.$hasHeader = !isSubheader;
+ $scope.$parent.$hasSubheader = isSubheader;
+ });
+ $scope.$on('$destroy', function() {
+ $scope.$parent.$hasHeader = $scope.$parent.$hasSubheader = null;
+ });
+ } else {
+ $scope.$watch(function() { return el.className; }, function(value) {
+ var isSubfooter = value.indexOf('bar-subfooter') !== -1;
+ $scope.$parent.$hasFooter = !isSubfooter;
+ $scope.$parent.$hasSubfooter = isSubfooter;
+ });
+ $scope.$on('$destroy', function() {
+ $scope.$parent.$hasFooter = $scope.$parent.$hasSubfooter = null;
+ });
+ $scope.$watch('$hasTabs', function(val) {
+ $element.toggleClass('has-tabs', !!val);
+ });
+ }
+ }
}
};
}];
diff --git a/js/ext/angular/src/directive/ionicContent.js b/js/ext/angular/src/directive/ionicContent.js
index 08ddc4aa33..f25443212a 100644
--- a/js/ext/angular/src/directive/ionicContent.js
+++ b/js/ext/angular/src/directive/ionicContent.js
@@ -44,9 +44,6 @@ angular.module('ionic.ui.content', ['ionic.ui.scroll'])
* directive, and infinite scrolling with the {@link ionic.directive:ionInfiniteScroll}
* directive.
*
- * Use the classes 'has-header', 'has-subheader', 'has-footer', and 'has-tabs'
- * to modify the positioning of the ion-content relative to surrounding elements.
- *
* @param {string=} controller-bind The scope variable to bind this element's scrollView's
* {@link ionic.controller:ionicScroll ionicScroll controller} to.
* Default: $scope.$ionicScrollController.
@@ -68,24 +65,31 @@ angular.module('ionic.ui.content', ['ionic.ui.scroll'])
function($parse, $timeout, $controller, $ionicBind) {
return {
restrict: 'E',
- replace: true,
- transclude: true,
require: '^?ionNavView',
scope: true,
- template:
- '
',
- compile: function(element, attr, transclude) {
- return {
- //Prelink so it can compile before other directives compile.
- //Then other directives can require ionicScrollCtrl
- pre: prelink
- };
+ compile: function(element, attr) {
+ element.addClass('scroll-content');
+ //We cannot transclude here because it breaks element.data() inheritance on compile
+ var innerElement = angular.element('');
+ innerElement.append(element.contents());
+ element.append(innerElement);
+
+ return { pre: prelink };
function prelink($scope, $element, $attr, navViewCtrl) {
- var clone, sc, scrollView, scrollCtrl,
- scrollContent = angular.element($element[0].querySelector('.scroll'));
+ var clone, sc, scrollView, scrollCtrl;
+
+ $scope.$watch(function() {
+ return ($scope.$hasHeader ? ' has-header' : '') +
+ ($scope.$hasSubheader ? ' has-subheader' : '') +
+ ($scope.$hasFooter ? ' has-footer' : '') +
+ ($scope.$hasSubfooter ? ' has-subfooter' : '') +
+ ($scope.$hasTabs ? ' has-tabs' : '') +
+ ($scope.$hasTabsTop ? ' has-tabs-top' : '');
+ }, function(className, oldClassName) {
+ $element.removeClass(oldClassName);
+ $element.addClass(className);
+ });
$ionicBind($scope, $attr, {
$onScroll: '&onScroll',
@@ -104,7 +108,7 @@ function($parse, $timeout, $controller, $ionicBind) {
if (angular.isDefined($attr.padding)) {
$scope.$watch($attr.padding, function(newVal) {
- scrollContent.toggleClass('padding', !!newVal);
+ innerElement.toggleClass('padding', !!newVal);
});
}
@@ -139,13 +143,6 @@ function($parse, $timeout, $controller, $ionicBind) {
scrollView = scrollCtrl.scrollView;
}
- transclude($scope, function(clone) {
- if (scrollCtrl) {
- clone.data('$$ionicScrollController', scrollCtrl);
- }
- scrollContent.append(clone);
- });
-
}
}
};
diff --git a/js/ext/angular/src/directive/ionicNavBar.js b/js/ext/angular/src/directive/ionicNavBar.js
index cd2b39dc42..1a05a738a8 100644
--- a/js/ext/angular/src/directive/ionicNavBar.js
+++ b/js/ext/angular/src/directive/ionicNavBar.js
@@ -215,20 +215,22 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
return {
restrict: 'E',
- replace: true,
- transclude: true,
controller: '$ionicNavBar',
- template:
- '',
- compile: function(tElement, tAttrs, transclude) {
+ scope: true,
+ compile: function(tElement, tAttrs) {
+ //We cannot transclude here because it breaks element.data() inheritance on compile
+ tElement
+ .addClass('bar bar-header nav-bar')
+ .append(
+ ' ' +
+ '
' +
+ '' +
+ ' ' +
+ '
'
+ );
- return function link($scope, $element, $attr, navBarCtrl) {
+ return { pre: prelink };
+ function prelink($scope, $element, $attr, navBarCtrl) {
navBarCtrl._headerBarView = new ionic.views.HeaderBar({
el: $element[0],
alignTitle: $attr.alignTitle || 'center'
@@ -237,23 +239,23 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
$parse($attr.controllerBind || '$ionicNavBarController')
.assign($scope, navBarCtrl);
- //Put transcluded content (usually a back button) before the rest
- transclude($scope, function(clone) {
- $element.prepend(clone);
- });
-
//defaults
$scope.backButtonShown = false;
$scope.shouldAnimate = true;
$scope.isReverse = false;
$scope.isInvisible = true;
+ $scope.$parent.$hasHeader = true;
- $scope.navBarClass = function() {
+ $scope.$watch(function() {
return ($scope.isReverse ? ' reverse' : '') +
($scope.isInvisible ? ' invisible' : '') +
(!$scope.shouldAnimate ? ' no-animation' : '');
- };
- };
+ }, function(className, oldClassName) {
+ $element.removeClass(oldClassName);
+ $element.addClass(className);
+ });
+
+ }
}
};
}])
@@ -311,32 +313,30 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
return {
restrict: 'E',
require: '^ionNavBar',
- replace: true,
- transclude: true,
- template:
- '',
- link: function($scope, $element, $attr, navBarCtrl) {
- $scope.$navBack = navBarCtrl.back;
- if (!$attr.ngClick) {
- $ionicNgClick($scope, $element, '$navBack($event)');
- }
-
- //If the current viewstate does not allow a back button,
- //always hide it.
- var deregisterListener = $scope.$parent.$on(
- '$viewHistory.historyChange',
- function(e, data) {
- $scope.hasBackButton = !!data.showBack;
+ compile: function(tElement, tAttrs) {
+ tElement.addClass('button back-button');
+ return function($scope, $element, $attr, navBarCtrl) {
+ $scope.$navBack = navBarCtrl.back;
+ if (!$attr.ngClick) {
+ $ionicNgClick($scope, $element, '$navBack($event)');
}
- );
- $scope.$on('$destroy', deregisterListener);
- //Make sure both that a backButton is allowed in the first place,
- //and that it is shown by the current view.
- $scope.$watch('!!(backButtonShown && hasBackButton)', function(val) {
- $element.toggleClass('hide', !val);
- });
+ //If the current viewstate does not allow a back button,
+ //always hide it.
+ var deregisterListener = $scope.$parent.$on(
+ '$viewHistory.historyChange',
+ function(e, data) {
+ $scope.hasBackButton = !!data.showBack;
+ }
+ );
+ $scope.$on('$destroy', deregisterListener);
+
+ //Make sure both that a backButton is allowed in the first place,
+ //and that it is shown by the current view.
+ $scope.$watch('!!(backButtonShown && hasBackButton)', function(val) {
+ $element.toggleClass('hide', !val);
+ });
+ };
}
};
}])
@@ -379,9 +379,8 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
.directive('ionNavButtons', ['$compile', '$animate', function($compile, $animate) {
return {
require: '^ionNavBar',
- transclude: true,
restrict: 'E',
- compile: function($element, $attrs, transclude) {
+ compile: function($element, $attrs) {
return function($scope, $element, $attrs, navBarCtrl) {
var navElement = $attrs.side === 'right' ?
navBarCtrl.rightButtonsElement :
@@ -391,13 +390,13 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
//so we can remove them all when this element dies -
//even if the buttons have changed through an ng-repeat or the like,
//we just remove their div parent and they are gone.
- var clone = angular.element('').append(transclude($scope));
- $animate.enter(clone, navElement);
+ var buttons = angular.element('
').append($element.contents().remove());
+ $animate.enter(buttons, navElement);
//When our ion-nav-buttons container is destroyed,
//destroy everything in the navbar
$scope.$on('$destroy', function() {
- $animate.leave(clone);
+ $animate.leave(buttons);
});
// The original element is just a completely empty
element.
diff --git a/js/ext/angular/src/directive/ionicPopup.js b/js/ext/angular/src/directive/ionicPopup.js
index b1902e6ecb..f841a4553b 100644
--- a/js/ext/angular/src/directive/ionicPopup.js
+++ b/js/ext/angular/src/directive/ionicPopup.js
@@ -23,6 +23,18 @@ angular.module('ionic.ui.popup', [])
replace: true,
transclude: true,
scope: true,
+ template:
+ '',
link: function($scope, $element, $attr) {
$ionicBind($scope, $attr, {
title: '@',
@@ -45,18 +57,7 @@ angular.module('ionic.ui.popup', [])
}
$scope.$onButtonTap({button: button, event: event});
}
- },
- template: ''
+ }
};
}]);
diff --git a/js/ext/angular/src/directive/ionicScroll.js b/js/ext/angular/src/directive/ionicScroll.js
index 73b6376194..dd45ff34db 100644
--- a/js/ext/angular/src/directive/ionicScroll.js
+++ b/js/ext/angular/src/directive/ionicScroll.js
@@ -26,9 +26,6 @@ angular.module('ionic.ui.scroll', [])
.directive('ionScroll', ['$parse', '$timeout', '$controller', function($parse, $timeout, $controller) {
return {
restrict: 'E',
- replace: true,
- template: '',
- transclude: true,
scope: {
direction: '@',
paging: '@',
@@ -39,23 +36,25 @@ angular.module('ionic.ui.scroll', [])
scrollbarY: '@',
},
controller: function() {},
- compile: function(element, attr, transclude) {
+ compile: function(element, attr) {
+ element.addClass('scroll-view');
- return {
- //Prelink so it can compile before other directives compile.
- //Then other directives can require ionicScrollCtrl
- pre: prelink
- };
+ //We cannot transclude here because it breaks element.data() inheritance on compile
+ var innerElement = angular.element('');
+ innerElement.append(element.contents());
+ element.append(innerElement);
+ return { pre: prelink };
function prelink($scope, $element, $attr) {
- var scrollView, scrollCtrl,
- sc = $element[0].children[0];
+ var scrollView, scrollCtrl;
- if(attr.padding == "true") {
- sc.classList.add('padding');
+ if (angular.isDefined($attr.padding)) {
+ $scope.$watch($attr.padding, function(newVal) {
+ innerElement.toggleClass('padding', !!newVal);
+ });
}
if($scope.$eval($scope.paging) === true) {
- sc.classList.add('scroll-paging');
+ innerElement.addClass('scroll-paging');
}
if(!$scope.direction) { $scope.direction = 'y'; }
diff --git a/js/ext/angular/src/directive/ionicSideMenu.js b/js/ext/angular/src/directive/ionicSideMenu.js
index 57be388b94..d46e9ae1af 100644
--- a/js/ext/angular/src/directive/ionicSideMenu.js
+++ b/js/ext/angular/src/directive/ionicSideMenu.js
@@ -157,8 +157,9 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie
restrict: 'AC',
require: '^ionSideMenus',
scope: true,
- compile: function(element, attr, transclude) {
- return function($scope, $element, $attr, sideMenuCtrl) {
+ compile: function(element, attr) {
+ return { pre: prelink };
+ function prelink($scope, $element, $attr, sideMenuCtrl) {
$element.addClass('menu-content');
@@ -248,7 +249,7 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie
$ionicGesture.off(releaseGesture, 'release', dragReleaseFn);
ionic.off('tap', contentTap, $element[0]);
});
- };
+ }
}
};
}])
@@ -282,14 +283,13 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie
return {
restrict: 'E',
require: '^ionSideMenus',
- replace: true,
- transclude: true,
scope: true,
- template: '',
- compile: function(element, attr, transclude) {
+ compile: function(element, attr) {
angular.isUndefined(attr.isEnabled) && attr.$set('isEnabled', 'true');
angular.isUndefined(attr.width) && attr.$set('width', '275');
+ element.addClass('menu menu-' + attr.side);
+
return function($scope, $element, $attr, sideMenuCtrl) {
$scope.side = $attr.side || 'left';
@@ -308,10 +308,6 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie
$scope.$watch($attr.isEnabled, function(val) {
sideMenu.setIsEnabled(!!val);
});
-
- transclude($scope, function(clone) {
- $element.append(clone);
- });
};
}
};
diff --git a/js/ext/angular/src/directive/ionicTabBar.js b/js/ext/angular/src/directive/ionicTabBar.js
index 8aaf1d5209..64904c8674 100644
--- a/js/ext/angular/src/directive/ionicTabBar.js
+++ b/js/ext/angular/src/directive/ionicTabBar.js
@@ -172,28 +172,33 @@ angular.module('ionic.ui.tabs', ['ionic.service.view'])
.directive('ionTabs', ['$ionicViewService', '$parse', function($ionicViewService, $parse) {
return {
restrict: 'E',
- replace: true,
scope: true,
- transclude: true,
controller: 'ionicTabs',
- template:
- '',
- compile: function(element, attr, transclude) {
+ compile: function(element, attr) {
+ element.addClass('view');
+ //We cannot transclude here because it breaks element.data() inheritance on compile
+ var innerElement = angular.element('');
+ innerElement.append(element.contents());
+ element.append(innerElement);
- return function link($scope, $element, $attr, tabsCtrl) {
+ return { pre: prelink };
+ function prelink($scope, $element, $attr, tabsCtrl) {
$parse(attr.model || '$ionicTabsController').assign($scope, tabsCtrl);
tabsCtrl.$scope = $scope;
tabsCtrl.$element = $element;
tabsCtrl.$tabsElement = angular.element($element[0].querySelector('.tabs'));
- transclude($scope, function(clone) {
- $element.append(clone);
+ var el = $element[0];
+ $scope.$watch(function() { return el.className; }, function(value) {
+ var isTabsTop = value.indexOf('tabs-top') !== -1;
+ $scope.$hasTabs = !isTabsTop;
+ $scope.$hasTabsTop = isTabsTop;
});
- };
+ $scope.$on('$destroy', function() {
+ $scope.$hasTabs = $scope.$hasTabsTop = null;
+ });
+ }
}
};
}])
diff --git a/js/ext/angular/test/directive/ionicBar.unit.js b/js/ext/angular/test/directive/ionicBar.unit.js
index 76211794c2..b3091e26db 100644
--- a/js/ext/angular/test/directive/ionicBar.unit.js
+++ b/js/ext/angular/test/directive/ionicBar.unit.js
@@ -55,11 +55,11 @@ describe('bar directives', function() {
angular.forEach([{
tag: 'ion-header-bar',
- element: 'header',
+ className: 'bar bar-header',
controllerBind: '$ionicHeaderBarController'
}, {
tag: 'ion-footer-bar',
- element: 'footer',
+ className: 'bar bar-footer',
controllerBind: '$ionicFooterBarController'
}], function(data) {
describe(data.tag, function() {
@@ -78,9 +78,50 @@ describe('bar directives', function() {
return el;
}
- it('should compile to ' + data.element, function() {
+ if (data.tag === 'ion-header-bar') {
+ it('$hasHeader $hasSubheader', function() {
+ var el = setup();
+ var scope = el.scope().$parent;
+ expect(scope.$hasHeader).toEqual(true);
+ expect(scope.$hasSubheader).toEqual(false);
+ el.addClass('bar-subheader');
+ scope.$apply();
+ expect(scope.$hasHeader).toEqual(false);
+ expect(scope.$hasSubheader).toEqual(true);
+ el.removeClass('bar-subheader');
+ scope.$apply();
+ expect(scope.$hasHeader).toEqual(true);
+ expect(scope.$hasSubheader).toEqual(false);
+ });
+ } else {
+ it('$hasFooter $hasSubheader', function() {
+ var el = setup();
+ var scope = el.scope().$parent;
+ expect(scope.$hasFooter).toEqual(true);
+ expect(scope.$hasSubfooter).toEqual(false);
+ el.addClass('bar-subfooter');
+ scope.$apply();
+ expect(scope.$hasFooter).toEqual(false);
+ expect(scope.$hasSubfooter).toEqual(true);
+ el.removeClass('bar-subfooter');
+ scope.$apply();
+ expect(scope.$hasFooter).toEqual(true);
+ expect(scope.$hasSubfooter).toEqual(false);
+ });
+ it('.has-tabs', function() {
+ var el = setup();
+ var scope = el.scope().$parent;
+ expect(el.hasClass('has-tabs')).toBe(false);
+ scope.$apply('$hasTabs = true');
+ expect(el.hasClass('has-tabs')).toBe(true);
+ scope.$apply('$hasTabs = false');
+ expect(el.hasClass('has-tabs')).toBe(false);
+ });
+ }
+
+ it('should compile to ' + data.className, function() {
var el = setup();
- expect(el[0].tagName.toLowerCase()).toBe(data.element);
+ expect(el.hasClass(data.className)).toBe(true);
});
it('should assign views.HeaderBar to default controllerBind', function() {
diff --git a/js/ext/angular/test/directive/ionicNavBar.unit.js b/js/ext/angular/test/directive/ionicNavBar.unit.js
index 95aafa385a..ca9b8d5604 100644
--- a/js/ext/angular/test/directive/ionicNavBar.unit.js
+++ b/js/ext/angular/test/directive/ionicNavBar.unit.js
@@ -229,6 +229,11 @@ describe('ionNavBar', function() {
expect(el.children().eq(0).html()).toBe('super content 4');
});
+ it('should $parent.$hasHeader', function() {
+ var el = setup();
+ expect(el.scope().$parent.$hasHeader).toBe(true);
+ });
+
it('should assign $scope.navBarController by default', function() {
var el = setup();
expect(el.controller('ionNavBar')).toBeTruthy(); //sanity
diff --git a/js/ext/angular/test/directive/ionicScroll.unit.js b/js/ext/angular/test/directive/ionicScroll.unit.js
index f2ae0519b4..a482ec7259 100644
--- a/js/ext/angular/test/directive/ionicScroll.unit.js
+++ b/js/ext/angular/test/directive/ionicScroll.unit.js
@@ -26,6 +26,7 @@ describe('Ionic Scroll Directive', function() {
it('should add padding classname', function() {
element = compile('')(scope);
+ scope.$apply();
expect(element.children().eq(0).hasClass('padding')).toEqual(true);
var scrollElement = element.find('.scroll');
expect(scrollElement.hasClass('padding')).toEqual(true);
diff --git a/js/ext/angular/test/directive/ionicTabBar.unit.js b/js/ext/angular/test/directive/ionicTabBar.unit.js
index e5a9d28ff2..7093d8605c 100644
--- a/js/ext/angular/test/directive/ionicTabBar.unit.js
+++ b/js/ext/angular/test/directive/ionicTabBar.unit.js
@@ -224,6 +224,21 @@ describe('tabs', function() {
return element;
}
+ it('should $hasTabs and $hasTabsTop', function() {
+ var el = setup();
+ var scope = el.scope();
+ expect(scope.$hasTabs).toBe(true);
+ expect(scope.$hasTabsTop).toBe(false);
+ el.addClass('tabs-top');
+ scope.$apply();
+ expect(scope.$hasTabs).toBe(false);
+ expect(scope.$hasTabsTop).toBe(true);
+ el.removeClass('tabs-top');
+ scope.$apply();
+ expect(scope.$hasTabs).toBe(true);
+ expect(scope.$hasTabsTop).toBe(false);
+ });
+
it('should bind controller to scope.$ionicTabsController by default', function() {
var el = setup();
expect(el.controller('ionTabs')).toBeTruthy(); //sanity
@@ -238,8 +253,7 @@ describe('tabs', function() {
it('should transclude content with same scope', function() {
var el = setup('', '');
- expect(el.children().eq(1).hasClass('content')).toBe(true);
- expect(el.children().eq(1).scope()).toBe(el.scope());
+ expect(el[0].querySelector('.tabs .content')).toBeTruthy();
});
});
diff --git a/js/ext/angular/test/list-fit.html b/js/ext/angular/test/list-fit.html
index ce29f6a1a1..ba78e419d8 100644
--- a/js/ext/angular/test/list-fit.html
+++ b/js/ext/angular/test/list-fit.html
@@ -16,7 +16,12 @@
Header!
-
-