Skip to content

Commit e94d400

Browse files
committed
feat(content): automatically add/remove has-* classes to content
Also, do manual transclusion on items that would be 'deep' directives - to fix a problem with transcluding & requiring parent elements. Closes #619
1 parent 4e60597 commit e94d400

14 files changed

+245
-154
lines changed

js/ext/angular/src/directive/ionicBar.js

+37-15
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,47 @@ angular.module('ionic.ui.header', ['ngAnimate', 'ngSanitize'])
112112
.directive('ionFooterBar', barDirective(false));
113113

114114
function barDirective(isHeader) {
115-
var BAR_TEMPLATE = isHeader ?
116-
'<header class="bar bar-header" ng-transclude></header>' :
117-
'<footer class="bar bar-footer" ng-transclude></footer>';
118-
var BAR_MODEL_DEFAULT = isHeader ?
119-
'$ionicHeaderBarController' :
120-
'$ionicFooterBarController';
121115
return ['$parse', function($parse) {
122116
return {
123117
restrict: 'E',
124-
replace: true,
125-
transclude: true,
126-
template: BAR_TEMPLATE,
127-
link: function($scope, $element, $attr) {
128-
var hb = new ionic.views.HeaderBar({
129-
el: $element[0],
130-
alignTitle: $attr.alignTitle || 'center'
131-
});
118+
compile: function($element, $attr) {
119+
$element.addClass(isHeader ? 'bar bar-header' : 'bar bar-footer');
120+
return { pre: prelink };
121+
function prelink($scope, $element, $attr) {
122+
var hb = new ionic.views.HeaderBar({
123+
el: $element[0],
124+
alignTitle: $attr.alignTitle || 'center'
125+
});
126+
127+
$parse($attr.controllerBind ||
128+
(isHeader ? '$ionicHeaderBarController' : '$ionicFooterBarController')
129+
).assign($scope, hb);
130+
131+
var el = $element[0];
132132

133-
$parse($attr.controllerBind || BAR_MODEL_DEFAULT).assign($scope, hb);
133+
if (isHeader) {
134+
$scope.$watch(function() { return el.className; }, function(value) {
135+
var isSubheader = value.indexOf('bar-subheader') !== -1;
136+
$scope.$parent.$hasHeader = !isSubheader;
137+
$scope.$parent.$hasSubheader = isSubheader;
138+
});
139+
$scope.$on('$destroy', function() {
140+
$scope.$parent.$hasHeader = $scope.$parent.$hasSubheader = null;
141+
});
142+
} else {
143+
$scope.$watch(function() { return el.className; }, function(value) {
144+
var isSubfooter = value.indexOf('bar-subfooter') !== -1;
145+
$scope.$parent.$hasFooter = !isSubfooter;
146+
$scope.$parent.$hasSubfooter = isSubfooter;
147+
});
148+
$scope.$on('$destroy', function() {
149+
$scope.$parent.$hasFooter = $scope.$parent.$hasSubfooter = null;
150+
});
151+
$scope.$watch('$hasTabs', function(val) {
152+
$element.toggleClass('has-tabs', !!val);
153+
});
154+
}
155+
}
134156
}
135157
};
136158
}];

js/ext/angular/src/directive/ionicContent.js

+22-25
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ angular.module('ionic.ui.content', ['ionic.ui.scroll'])
4444
* directive, and infinite scrolling with the {@link ionic.directive:ionInfiniteScroll}
4545
* directive.
4646
*
47-
* Use the classes 'has-header', 'has-subheader', 'has-footer', and 'has-tabs'
48-
* to modify the positioning of the ion-content relative to surrounding elements.
49-
*
5047
* @param {string=} controller-bind The scope variable to bind this element's scrollView's
5148
* {@link ionic.controller:ionicScroll ionicScroll controller} to.
5249
* Default: $scope.$ionicScrollController.
@@ -68,24 +65,31 @@ angular.module('ionic.ui.content', ['ionic.ui.scroll'])
6865
function($parse, $timeout, $controller, $ionicBind) {
6966
return {
7067
restrict: 'E',
71-
replace: true,
72-
transclude: true,
7368
require: '^?ionNavView',
7469
scope: true,
75-
template:
76-
'<div class="scroll-content">' +
77-
'<div class="scroll"></div>' +
78-
'</div>',
79-
compile: function(element, attr, transclude) {
80-
return {
81-
//Prelink <ion-content> so it can compile before other directives compile.
82-
//Then other directives can require ionicScrollCtrl
83-
pre: prelink
84-
};
70+
compile: function(element, attr) {
71+
element.addClass('scroll-content');
8572

73+
//We cannot transclude here because it breaks element.data() inheritance on compile
74+
var innerElement = angular.element('<div class="scroll"></div>');
75+
innerElement.append(element.contents());
76+
element.append(innerElement);
77+
78+
return { pre: prelink };
8679
function prelink($scope, $element, $attr, navViewCtrl) {
87-
var clone, sc, scrollView, scrollCtrl,
88-
scrollContent = angular.element($element[0].querySelector('.scroll'));
80+
var clone, sc, scrollView, scrollCtrl;
81+
82+
$scope.$watch(function() {
83+
return ($scope.$hasHeader ? ' has-header' : '') +
84+
($scope.$hasSubheader ? ' has-subheader' : '') +
85+
($scope.$hasFooter ? ' has-footer' : '') +
86+
($scope.$hasSubfooter ? ' has-subfooter' : '') +
87+
($scope.$hasTabs ? ' has-tabs' : '') +
88+
($scope.$hasTabsTop ? ' has-tabs-top' : '');
89+
}, function(className, oldClassName) {
90+
$element.removeClass(oldClassName);
91+
$element.addClass(className);
92+
});
8993

9094
$ionicBind($scope, $attr, {
9195
$onScroll: '&onScroll',
@@ -104,7 +108,7 @@ function($parse, $timeout, $controller, $ionicBind) {
104108

105109
if (angular.isDefined($attr.padding)) {
106110
$scope.$watch($attr.padding, function(newVal) {
107-
scrollContent.toggleClass('padding', !!newVal);
111+
innerElement.toggleClass('padding', !!newVal);
108112
});
109113
}
110114

@@ -139,13 +143,6 @@ function($parse, $timeout, $controller, $ionicBind) {
139143
scrollView = scrollCtrl.scrollView;
140144
}
141145

142-
transclude($scope, function(clone) {
143-
if (scrollCtrl) {
144-
clone.data('$$ionicScrollController', scrollCtrl);
145-
}
146-
scrollContent.append(clone);
147-
});
148-
149146
}
150147
}
151148
};

js/ext/angular/src/directive/ionicNavBar.js

+48-49
Original file line numberDiff line numberDiff line change
@@ -215,20 +215,22 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
215215

216216
return {
217217
restrict: 'E',
218-
replace: true,
219-
transclude: true,
220218
controller: '$ionicNavBar',
221-
template:
222-
'<header class="bar bar-header nav-bar{{navBarClass()}}">' +
223-
'<div class="buttons left-buttons"> ' +
224-
'</div>' +
225-
'<h1 ng-bind-html="title" class="title"></h1>' +
226-
'<div class="buttons right-buttons"> ' +
227-
'</div>' +
228-
'</header>',
229-
compile: function(tElement, tAttrs, transclude) {
219+
scope: true,
220+
compile: function(tElement, tAttrs) {
221+
//We cannot transclude here because it breaks element.data() inheritance on compile
222+
tElement
223+
.addClass('bar bar-header nav-bar')
224+
.append(
225+
'<div class="buttons left-buttons"> ' +
226+
'</div>' +
227+
'<h1 ng-bind-html="title" class="title"></h1>' +
228+
'<div class="buttons right-buttons"> ' +
229+
'</div>'
230+
);
230231

231-
return function link($scope, $element, $attr, navBarCtrl) {
232+
return { pre: prelink };
233+
function prelink($scope, $element, $attr, navBarCtrl) {
232234
navBarCtrl._headerBarView = new ionic.views.HeaderBar({
233235
el: $element[0],
234236
alignTitle: $attr.alignTitle || 'center'
@@ -237,23 +239,23 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
237239
$parse($attr.controllerBind || '$ionicNavBarController')
238240
.assign($scope, navBarCtrl);
239241

240-
//Put transcluded content (usually a back button) before the rest
241-
transclude($scope, function(clone) {
242-
$element.prepend(clone);
243-
});
244-
245242
//defaults
246243
$scope.backButtonShown = false;
247244
$scope.shouldAnimate = true;
248245
$scope.isReverse = false;
249246
$scope.isInvisible = true;
247+
$scope.$parent.$hasHeader = true;
250248

251-
$scope.navBarClass = function() {
249+
$scope.$watch(function() {
252250
return ($scope.isReverse ? ' reverse' : '') +
253251
($scope.isInvisible ? ' invisible' : '') +
254252
(!$scope.shouldAnimate ? ' no-animation' : '');
255-
};
256-
};
253+
}, function(className, oldClassName) {
254+
$element.removeClass(oldClassName);
255+
$element.addClass(className);
256+
});
257+
258+
}
257259
}
258260
};
259261
}])
@@ -311,32 +313,30 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
311313
return {
312314
restrict: 'E',
313315
require: '^ionNavBar',
314-
replace: true,
315-
transclude: true,
316-
template:
317-
'<button class="button back-button" ng-transclude>' +
318-
'</button>',
319-
link: function($scope, $element, $attr, navBarCtrl) {
320-
$scope.$navBack = navBarCtrl.back;
321-
if (!$attr.ngClick) {
322-
$ionicNgClick($scope, $element, '$navBack($event)');
323-
}
324-
325-
//If the current viewstate does not allow a back button,
326-
//always hide it.
327-
var deregisterListener = $scope.$parent.$on(
328-
'$viewHistory.historyChange',
329-
function(e, data) {
330-
$scope.hasBackButton = !!data.showBack;
316+
compile: function(tElement, tAttrs) {
317+
tElement.addClass('button back-button');
318+
return function($scope, $element, $attr, navBarCtrl) {
319+
$scope.$navBack = navBarCtrl.back;
320+
if (!$attr.ngClick) {
321+
$ionicNgClick($scope, $element, '$navBack($event)');
331322
}
332-
);
333-
$scope.$on('$destroy', deregisterListener);
334323

335-
//Make sure both that a backButton is allowed in the first place,
336-
//and that it is shown by the current view.
337-
$scope.$watch('!!(backButtonShown && hasBackButton)', function(val) {
338-
$element.toggleClass('hide', !val);
339-
});
324+
//If the current viewstate does not allow a back button,
325+
//always hide it.
326+
var deregisterListener = $scope.$parent.$on(
327+
'$viewHistory.historyChange',
328+
function(e, data) {
329+
$scope.hasBackButton = !!data.showBack;
330+
}
331+
);
332+
$scope.$on('$destroy', deregisterListener);
333+
334+
//Make sure both that a backButton is allowed in the first place,
335+
//and that it is shown by the current view.
336+
$scope.$watch('!!(backButtonShown && hasBackButton)', function(val) {
337+
$element.toggleClass('hide', !val);
338+
});
339+
};
340340
}
341341
};
342342
}])
@@ -379,9 +379,8 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
379379
.directive('ionNavButtons', ['$compile', '$animate', function($compile, $animate) {
380380
return {
381381
require: '^ionNavBar',
382-
transclude: true,
383382
restrict: 'E',
384-
compile: function($element, $attrs, transclude) {
383+
compile: function($element, $attrs) {
385384
return function($scope, $element, $attrs, navBarCtrl) {
386385
var navElement = $attrs.side === 'right' ?
387386
navBarCtrl.rightButtonsElement :
@@ -391,13 +390,13 @@ function($ionicViewService, $rootScope, $animate, $compile, $parse) {
391390
//so we can remove them all when this element dies -
392391
//even if the buttons have changed through an ng-repeat or the like,
393392
//we just remove their div parent and they are gone.
394-
var clone = angular.element('<div>').append(transclude($scope));
395-
$animate.enter(clone, navElement);
393+
var buttons = angular.element('<div>').append($element.contents().remove());
394+
$animate.enter(buttons, navElement);
396395

397396
//When our ion-nav-buttons container is destroyed,
398397
//destroy everything in the navbar
399398
$scope.$on('$destroy', function() {
400-
$animate.leave(clone);
399+
$animate.leave(buttons);
401400
});
402401

403402
// The original element is just a completely empty <ion-nav-buttons> element.

js/ext/angular/src/directive/ionicPopup.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ angular.module('ionic.ui.popup', [])
2323
replace: true,
2424
transclude: true,
2525
scope: true,
26+
template:
27+
'<div class="popup">' +
28+
'<div class="popup-head">' +
29+
'<h3 class="popup-title" ng-bind-html="title"></h3>' +
30+
'<h5 class="popup-sub-title" ng-bind-html="subTitle" ng-if="subTitle"></h5>' +
31+
'</div>' +
32+
'<div class="popup-body" ng-transclude>' +
33+
'</div>' +
34+
'<div class="popup-buttons row">' +
35+
'<button ng-repeat="button in buttons" ng-click="_buttonTapped(button, $event)" class="button col" ng-class="button.type || \'button-default\'" ng-bind-html="button.text"></button>' +
36+
'</div>' +
37+
'</div>',
2638
link: function($scope, $element, $attr) {
2739
$ionicBind($scope, $attr, {
2840
title: '@',
@@ -45,18 +57,7 @@ angular.module('ionic.ui.popup', [])
4557
}
4658
$scope.$onButtonTap({button: button, event: event});
4759
}
48-
},
49-
template: '<div class="popup">' +
50-
'<div class="popup-head">' +
51-
'<h3 class="popup-title" ng-bind-html="title"></h3>' +
52-
'<h5 class="popup-sub-title" ng-bind-html="subTitle" ng-if="subTitle"></h5>' +
53-
'</div>' +
54-
'<div class="popup-body" ng-transclude>' +
55-
'</div>' +
56-
'<div class="popup-buttons row">' +
57-
'<button ng-repeat="button in buttons" ng-click="_buttonTapped(button, $event)" class="button col" ng-class="button.type || \'button-default\'" ng-bind-html="button.text"></button>' +
58-
'</div>' +
59-
'</div>'
60+
}
6061
};
6162
}]);
6263

js/ext/angular/src/directive/ionicScroll.js

+13-14
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ angular.module('ionic.ui.scroll', [])
2626
.directive('ionScroll', ['$parse', '$timeout', '$controller', function($parse, $timeout, $controller) {
2727
return {
2828
restrict: 'E',
29-
replace: true,
30-
template: '<div class="scroll-view"><div class="scroll" ng-transclude></div></div>',
31-
transclude: true,
3229
scope: {
3330
direction: '@',
3431
paging: '@',
@@ -39,23 +36,25 @@ angular.module('ionic.ui.scroll', [])
3936
scrollbarY: '@',
4037
},
4138
controller: function() {},
42-
compile: function(element, attr, transclude) {
39+
compile: function(element, attr) {
40+
element.addClass('scroll-view');
4341

44-
return {
45-
//Prelink <ion-scroll> so it can compile before other directives compile.
46-
//Then other directives can require ionicScrollCtrl
47-
pre: prelink
48-
};
42+
//We cannot transclude here because it breaks element.data() inheritance on compile
43+
var innerElement = angular.element('<div class="scroll"></div>');
44+
innerElement.append(element.contents());
45+
element.append(innerElement);
4946

47+
return { pre: prelink };
5048
function prelink($scope, $element, $attr) {
51-
var scrollView, scrollCtrl,
52-
sc = $element[0].children[0];
49+
var scrollView, scrollCtrl;
5350

54-
if(attr.padding == "true") {
55-
sc.classList.add('padding');
51+
if (angular.isDefined($attr.padding)) {
52+
$scope.$watch($attr.padding, function(newVal) {
53+
innerElement.toggleClass('padding', !!newVal);
54+
});
5655
}
5756
if($scope.$eval($scope.paging) === true) {
58-
sc.classList.add('scroll-paging');
57+
innerElement.addClass('scroll-paging');
5958
}
6059

6160
if(!$scope.direction) { $scope.direction = 'y'; }

0 commit comments

Comments
 (0)