Skip to content

Commit 2c3f1c9

Browse files
committed
feat($ionicBody): service to simplify body ele interaction
Many services/directives have to interact with the body element, and each one has to write the same long code. The $ionicBody service provides some useful methods to clean up and reduce redundant code.
1 parent b69aa54 commit 2c3f1c9

File tree

10 files changed

+203
-45
lines changed

10 files changed

+203
-45
lines changed

js/angular/controller/sideMenuController.js

+6-12
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ IonicModule
44
'$attrs',
55
'$ionicSideMenuDelegate',
66
'$ionicPlatform',
7-
'$document',
8-
function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $document) {
7+
'$ionicBody',
8+
function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $ionicBody) {
99
var self = this;
1010
var rightShowing, leftShowing, isDragging;
1111
var startX, lastX, offsetX, isAsideExposed;
@@ -132,11 +132,9 @@ function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $document) {
132132
self.openAmount(self.right.width * p);
133133
}
134134

135-
if(percentage !== 0) {
136-
$document[0].body.classList.add('menu-open');
137-
} else {
138-
$document[0].body.classList.remove('menu-open');
139-
}
135+
// add the CSS class "menu-open" if the percentage does not
136+
// equal 0, otherwise remove the class from the body element
137+
$ionicBody.enableClass( (percentage !== 0), 'menu-open');
140138
};
141139

142140
/**
@@ -269,11 +267,7 @@ function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $document) {
269267
};
270268

271269
self.activeAsideResizing = function(isResizing) {
272-
if(isResizing) {
273-
$document[0].body.classList.add('aside-resizing');
274-
} else {
275-
$document[0].body.classList.remove('aside-resizing');
276-
}
270+
$ionicBody.enableClass(isResizing, 'aside-resizing');
277271
};
278272

279273
// End a drag with the given event

js/angular/directive/sideMenus.js

+3-12
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,7 @@ IonicModule
6464
* with {@link ionic.service:$ionicSideMenuDelegate}.
6565
*
6666
*/
67-
.directive('ionSideMenus', ['$document', function($document) {
68-
69-
var ASIDE_OPEN_CSS = 'aside-open';
70-
67+
.directive('ionSideMenus', ['$ionicBody', function($ionicBody) {
7168
return {
7269
restrict: 'ECA',
7370
controller: '$ionicSideMenus',
@@ -76,21 +73,15 @@ IonicModule
7673

7774
return { pre: prelink };
7875
function prelink($scope) {
79-
var bodyClassList = $document[0].body.classList;
8076

8177
$scope.$on('$ionicExposeAside', function(evt, isAsideExposed){
8278
if(!$scope.$exposeAside) $scope.$exposeAside = {};
8379
$scope.$exposeAside.active = isAsideExposed;
84-
if(isAsideExposed) {
85-
bodyClassList.add(ASIDE_OPEN_CSS);
86-
} else {
87-
bodyClassList.remove(ASIDE_OPEN_CSS);
88-
}
80+
$ionicBody.enableClass(isAsideExposed, 'aside-open');
8981
});
9082

9183
$scope.$on('$destroy', function(){
92-
bodyClassList.remove('menu-open');
93-
bodyClassList.remove(ASIDE_OPEN_CSS);
84+
$ionicBody.removeClass('menu-open', 'aside-open');
9485
});
9586

9687
}

js/angular/service/actionSheet.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@
5151
IonicModule
5252
.factory('$ionicActionSheet', [
5353
'$rootScope',
54-
'$document',
5554
'$compile',
5655
'$animate',
5756
'$timeout',
5857
'$ionicTemplateLoader',
5958
'$ionicPlatform',
60-
function($rootScope, $document, $compile, $animate, $timeout, $ionicTemplateLoader, $ionicPlatform) {
59+
'$ionicBody',
60+
function($rootScope, $compile, $animate, $timeout, $ionicTemplateLoader, $ionicPlatform, $ionicBody) {
6161

6262
return {
6363
show: actionSheet
@@ -119,7 +119,7 @@ function($rootScope, $document, $compile, $animate, $timeout, $ionicTemplateLoad
119119

120120
scope.removed = true;
121121
sheetEl.removeClass('action-sheet-up');
122-
$document[0].body.classList.remove('action-sheet-open');
122+
$ionicBody.removeClass('action-sheet-open');
123123
scope.$deregisterBackButton();
124124
stateChangeListenDone();
125125

@@ -135,8 +135,8 @@ function($rootScope, $document, $compile, $animate, $timeout, $ionicTemplateLoad
135135
scope.showSheet = function(done) {
136136
if (scope.removed) return;
137137

138-
$document[0].body.appendChild(element[0]);
139-
$document[0].body.classList.add('action-sheet-open');
138+
$ionicBody.append(element)
139+
.addClass('action-sheet-open');
140140

141141
$animate.addClass(element, 'active', function() {
142142
if (scope.removed) return;

js/angular/service/body.js

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @ngdoc service
3+
* @name $ionicBody
4+
* @module ionic
5+
* @description An angular utility service to easily and efficiently
6+
* add and remove CSS classes from the document's body element.
7+
*/
8+
IonicModule
9+
.factory('$ionicBody', ['$document', function($document) {
10+
return {
11+
/**
12+
* @ngdoc method
13+
* @name $ionicBody#add
14+
* @description Add a class to the document's body element.
15+
* @param {string} class Each argument will be added to the body element.
16+
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
17+
*/
18+
addClass: function() {
19+
for(var x=0; x<arguments.length; x++) {
20+
$document[0].body.classList.add(arguments[x]);
21+
}
22+
return this;
23+
},
24+
/**
25+
* @ngdoc method
26+
* @name $ionicBody#removeClass
27+
* @description Remove a class from the document's body element.
28+
* @param {string} class Each argument will be removed from the body element.
29+
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
30+
*/
31+
removeClass: function() {
32+
for(var x=0; x<arguments.length; x++) {
33+
$document[0].body.classList.remove(arguments[x]);
34+
}
35+
return this;
36+
},
37+
/**
38+
* @ngdoc method
39+
* @name $ionicBody#enableClass
40+
* @description Similar to the `add` method, except the first parameter accepts a boolean
41+
* value determining if the class should be added or removed. Rather than writing user code,
42+
* such as "if true then add the class, else then remove the class", this method can be
43+
* given a true or false value which reduces redundant code.
44+
* @param {boolean} shouldEnableClass A true/false value if the class should be added or removed.
45+
* @param {string} class Each remaining argument would be added or removed depending on
46+
* the first argument.
47+
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
48+
*/
49+
enableClass: function(shouldEnableClass) {
50+
var args = Array.prototype.slice.call(arguments).slice(1);
51+
if(shouldEnableClass) {
52+
this.addClass.apply(this, args);
53+
} else {
54+
this.removeClass.apply(this, args);
55+
}
56+
return this;
57+
},
58+
/**
59+
* @ngdoc method
60+
* @name $ionicBody#append
61+
* @description Append a child to the document's body.
62+
* @param {element} element The element to be appended to the body. The passed in element
63+
* can be either a jqLite element, or a DOM element.
64+
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
65+
*/
66+
append: function(ele) {
67+
$document[0].body.appendChild( ele.length ? ele[0] : ele );
68+
return this;
69+
},
70+
/**
71+
* @ngdoc method
72+
* @name $ionicBody#get
73+
* @description Get the document's body element.
74+
* @returns {element} Returns the document's body element.
75+
*/
76+
get: function() {
77+
return $document[0].body;
78+
}
79+
};
80+
}]);

js/angular/service/loading.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ IonicModule
5858
})
5959
.factory('$ionicLoading', [
6060
'$ionicLoadingConfig',
61-
'$document',
61+
'$ionicBody',
6262
'$ionicTemplateLoader',
6363
'$ionicBackdrop',
6464
'$timeout',
6565
'$q',
6666
'$log',
6767
'$compile',
6868
'$ionicPlatform',
69-
function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $ionicPlatform) {
69+
function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $ionicPlatform) {
7070

7171
var loaderInstance;
7272
//default values
@@ -104,7 +104,7 @@ function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $
104104
if (!loaderInstance) {
105105
loaderInstance = $ionicTemplateLoader.compile({
106106
template: LOADING_TPL,
107-
appendTo: $document[0].body
107+
appendTo: $ionicBody.get()
108108
})
109109
.then(function(loader) {
110110
var self = loader;
@@ -144,8 +144,10 @@ function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $
144144
if (self.isShown) {
145145
self.element.addClass('visible');
146146
ionic.requestAnimationFrame(function() {
147-
self.isShown && self.element.addClass('active');
148-
self.isShown && $document[0].body.classList.add('loading-active');
147+
if(self.isShown) {
148+
self.element.addClass('active');
149+
$ionicBody.addClass('loading-active');
150+
}
149151
});
150152
}
151153
});
@@ -159,7 +161,7 @@ function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $
159161
$ionicBackdrop.getElement().removeClass('backdrop-loading');
160162
}
161163
self.element.removeClass('active');
162-
$document[0].body.classList.remove('loading-active');
164+
$ionicBody.removeClass('loading-active');
163165
setTimeout(function() {
164166
!self.isShown && self.element.removeClass('visible');
165167
}, 200);

js/angular/service/modal.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@
6161
IonicModule
6262
.factory('$ionicModal', [
6363
'$rootScope',
64-
'$document',
64+
'$ionicBody',
6565
'$compile',
6666
'$timeout',
6767
'$ionicPlatform',
6868
'$ionicTemplateLoader',
6969
'$q',
7070
'$log',
71-
function($rootScope, $document, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $q, $log) {
71+
function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $q, $log) {
7272

7373
/**
7474
* @ngdoc controller
@@ -124,12 +124,12 @@ function($rootScope, $document, $compile, $timeout, $ionicPlatform, $ionicTempla
124124

125125
self.el.classList.remove('hide');
126126
$timeout(function(){
127-
$document[0].body.classList.add(self.viewType + '-open');
127+
$ionicBody.addClass(self.viewType + '-open');
128128
}, 400);
129129

130130
if(!self.el.parentElement) {
131131
modalEl.addClass(self.animation);
132-
$document[0].body.appendChild(self.el);
132+
$ionicBody.append(self.el);
133133
}
134134

135135
if(target && self.positionView) {
@@ -192,7 +192,7 @@ function($rootScope, $document, $compile, $timeout, $ionicPlatform, $ionicTempla
192192
ionic.views.Modal.prototype.hide.call(self);
193193

194194
return $timeout(function(){
195-
$document[0].body.classList.remove(self.viewType + '-open');
195+
$ionicBody.removeClass(self.viewType + '-open');
196196
self.el.classList.add('hide');
197197
}, self.hideDelay || 500);
198198
},

js/angular/service/popup.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ IonicModule
110110
'$q',
111111
'$timeout',
112112
'$rootScope',
113-
'$document',
113+
'$ionicBody',
114114
'$compile',
115115
'$ionicPlatform',
116-
function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $document, $compile, $ionicPlatform) {
116+
function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $ionicBody, $compile, $ionicPlatform) {
117117
//TODO allow this to be configured
118118
var config = {
119119
stackPushDelay: 75
@@ -278,7 +278,7 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $docume
278278
var popupPromise = $ionicTemplateLoader.compile({
279279
template: POPUP_TPL,
280280
scope: options.scope && options.scope.$new(),
281-
appendTo: $document[0].body
281+
appendTo: $ionicBody.get()
282282
});
283283
var contentPromise = options.templateUrl ?
284284
$ionicTemplateLoader.load(options.templateUrl) :
@@ -370,7 +370,7 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $docume
370370
.then(function(popup) {
371371
if (!previousPopup) {
372372
//Add popup-open & backdrop if this is first popup
373-
document.body.classList.add('popup-open');
373+
$ionicBody.addClass('popup-open');
374374
$ionicBackdrop.retain();
375375
//only show the backdrop on the first popup
376376
$ionicPopup._backButtonActionDone = $ionicPlatform.registerBackButtonAction(
@@ -398,7 +398,7 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $docume
398398
previousPopup.show();
399399
} else {
400400
//Remove popup-open & backdrop if this is last popup
401-
document.body.classList.remove('popup-open');
401+
$ionicBody.removeClass('popup-open');
402402
$ionicBackdrop.release();
403403
($ionicPopup._backButtonActionDone || angular.noop)();
404404
}

test/unit/angular/controller/sideMenuController.unit.js

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ describe('$ionicSideMenus controller', function() {
8484
expect(ctrl.getOpenPercentage()).toEqual(-50);
8585
});
8686

87+
it('should add/remove menu-open from the body class', inject(function($document) {
88+
expect($document[0].body.classList.contains('menu-open')).toEqual(false);
89+
ctrl.openPercentage(100);
90+
expect($document[0].body.classList.contains('menu-open')).toEqual(true);
91+
ctrl.openPercentage(0);
92+
expect($document[0].body.classList.contains('menu-open')).toEqual(false);
93+
}));
94+
8795
// Open
8896
it('should toggle left', function() {
8997
ctrl.toggleLeft();

test/unit/angular/directive/sideMenu.unit.js

+12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ describe('Ionic Angular Side Menu', function() {
4040
expect(sideMenuController.isAsideExposed()).toBe(false);
4141
}));
4242

43+
it('should add/remove "aside-resizing" from the body tag when using activeAsideResizing', inject(function($compile, $rootScope, $document) {
44+
var el = $compile('<ion-side-menus><ion-side-menu></><ion-side-menu-content></ion-side-menu-content></ion-side-menus>')($rootScope.$new());
45+
$rootScope.$apply();
46+
var sideMenuController = el.controller('ionSideMenus');
47+
48+
expect($document[0].body.classList.contains('aside-resizing')).toEqual(false);
49+
sideMenuController.activeAsideResizing(true);
50+
expect($document[0].body.classList.contains('aside-resizing')).toEqual(true);
51+
sideMenuController.activeAsideResizing(false);
52+
expect($document[0].body.classList.contains('aside-resizing')).toEqual(false);
53+
}));
54+
4355
it('should emit $ionicexposeAside', inject(function($compile, $rootScope) {
4456
var el = $compile('<ion-side-menus><ion-side-menu></><ion-side-menu-content></ion-side-menu-content></ion-side-menus>')($rootScope.$new());
4557
$rootScope.$apply();

0 commit comments

Comments
 (0)