Skip to content

Commit

Permalink
feat($ionicBody): service to simplify body ele interaction
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
adamdbradley committed Aug 27, 2014
1 parent b69aa54 commit 2c3f1c9
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 45 deletions.
18 changes: 6 additions & 12 deletions js/angular/controller/sideMenuController.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ IonicModule
'$attrs',
'$ionicSideMenuDelegate',
'$ionicPlatform',
'$document',
function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $document) {
'$ionicBody',
function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $ionicBody) {
var self = this;
var rightShowing, leftShowing, isDragging;
var startX, lastX, offsetX, isAsideExposed;
Expand Down Expand Up @@ -132,11 +132,9 @@ function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $document) {
self.openAmount(self.right.width * p);
}

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

/**
Expand Down Expand Up @@ -269,11 +267,7 @@ function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $document) {
};

self.activeAsideResizing = function(isResizing) {
if(isResizing) {
$document[0].body.classList.add('aside-resizing');
} else {
$document[0].body.classList.remove('aside-resizing');
}
$ionicBody.enableClass(isResizing, 'aside-resizing');
};

// End a drag with the given event
Expand Down
15 changes: 3 additions & 12 deletions js/angular/directive/sideMenus.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,7 @@ IonicModule
* with {@link ionic.service:$ionicSideMenuDelegate}.
*
*/
.directive('ionSideMenus', ['$document', function($document) {

var ASIDE_OPEN_CSS = 'aside-open';

.directive('ionSideMenus', ['$ionicBody', function($ionicBody) {
return {
restrict: 'ECA',
controller: '$ionicSideMenus',
Expand All @@ -76,21 +73,15 @@ IonicModule

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

$scope.$on('$ionicExposeAside', function(evt, isAsideExposed){
if(!$scope.$exposeAside) $scope.$exposeAside = {};
$scope.$exposeAside.active = isAsideExposed;
if(isAsideExposed) {
bodyClassList.add(ASIDE_OPEN_CSS);
} else {
bodyClassList.remove(ASIDE_OPEN_CSS);
}
$ionicBody.enableClass(isAsideExposed, 'aside-open');
});

$scope.$on('$destroy', function(){
bodyClassList.remove('menu-open');
bodyClassList.remove(ASIDE_OPEN_CSS);
$ionicBody.removeClass('menu-open', 'aside-open');
});

}
Expand Down
10 changes: 5 additions & 5 deletions js/angular/service/actionSheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@
IonicModule
.factory('$ionicActionSheet', [
'$rootScope',
'$document',
'$compile',
'$animate',
'$timeout',
'$ionicTemplateLoader',
'$ionicPlatform',
function($rootScope, $document, $compile, $animate, $timeout, $ionicTemplateLoader, $ionicPlatform) {
'$ionicBody',
function($rootScope, $compile, $animate, $timeout, $ionicTemplateLoader, $ionicPlatform, $ionicBody) {

return {
show: actionSheet
Expand Down Expand Up @@ -119,7 +119,7 @@ function($rootScope, $document, $compile, $animate, $timeout, $ionicTemplateLoad

scope.removed = true;
sheetEl.removeClass('action-sheet-up');
$document[0].body.classList.remove('action-sheet-open');
$ionicBody.removeClass('action-sheet-open');
scope.$deregisterBackButton();
stateChangeListenDone();

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

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

$animate.addClass(element, 'active', function() {
if (scope.removed) return;
Expand Down
80 changes: 80 additions & 0 deletions js/angular/service/body.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @ngdoc service
* @name $ionicBody
* @module ionic
* @description An angular utility service to easily and efficiently
* add and remove CSS classes from the document's body element.
*/
IonicModule
.factory('$ionicBody', ['$document', function($document) {
return {
/**
* @ngdoc method
* @name $ionicBody#add
* @description Add a class to the document's body element.
* @param {string} class Each argument will be added to the body element.
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
*/
addClass: function() {
for(var x=0; x<arguments.length; x++) {
$document[0].body.classList.add(arguments[x]);
}
return this;
},
/**
* @ngdoc method
* @name $ionicBody#removeClass
* @description Remove a class from the document's body element.
* @param {string} class Each argument will be removed from the body element.
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
*/
removeClass: function() {
for(var x=0; x<arguments.length; x++) {
$document[0].body.classList.remove(arguments[x]);
}
return this;
},
/**
* @ngdoc method
* @name $ionicBody#enableClass
* @description Similar to the `add` method, except the first parameter accepts a boolean
* value determining if the class should be added or removed. Rather than writing user code,
* such as "if true then add the class, else then remove the class", this method can be
* given a true or false value which reduces redundant code.
* @param {boolean} shouldEnableClass A true/false value if the class should be added or removed.
* @param {string} class Each remaining argument would be added or removed depending on
* the first argument.
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
*/
enableClass: function(shouldEnableClass) {
var args = Array.prototype.slice.call(arguments).slice(1);
if(shouldEnableClass) {
this.addClass.apply(this, args);
} else {
this.removeClass.apply(this, args);
}
return this;
},
/**
* @ngdoc method
* @name $ionicBody#append
* @description Append a child to the document's body.
* @param {element} element The element to be appended to the body. The passed in element
* can be either a jqLite element, or a DOM element.
* @returns {$ionicBody} The $ionicBody service so methods can be chained.
*/
append: function(ele) {
$document[0].body.appendChild( ele.length ? ele[0] : ele );
return this;
},
/**
* @ngdoc method
* @name $ionicBody#get
* @description Get the document's body element.
* @returns {element} Returns the document's body element.
*/
get: function() {
return $document[0].body;
}
};
}]);
14 changes: 8 additions & 6 deletions js/angular/service/loading.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ IonicModule
})
.factory('$ionicLoading', [
'$ionicLoadingConfig',
'$document',
'$ionicBody',
'$ionicTemplateLoader',
'$ionicBackdrop',
'$timeout',
'$q',
'$log',
'$compile',
'$ionicPlatform',
function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $ionicPlatform) {
function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $ionicPlatform) {

var loaderInstance;
//default values
Expand Down Expand Up @@ -104,7 +104,7 @@ function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $
if (!loaderInstance) {
loaderInstance = $ionicTemplateLoader.compile({
template: LOADING_TPL,
appendTo: $document[0].body
appendTo: $ionicBody.get()
})
.then(function(loader) {
var self = loader;
Expand Down Expand Up @@ -144,8 +144,10 @@ function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $
if (self.isShown) {
self.element.addClass('visible');
ionic.requestAnimationFrame(function() {
self.isShown && self.element.addClass('active');
self.isShown && $document[0].body.classList.add('loading-active');
if(self.isShown) {
self.element.addClass('active');
$ionicBody.addClass('loading-active');
}
});
}
});
Expand All @@ -159,7 +161,7 @@ function($ionicLoadingConfig, $document, $ionicTemplateLoader, $ionicBackdrop, $
$ionicBackdrop.getElement().removeClass('backdrop-loading');
}
self.element.removeClass('active');
$document[0].body.classList.remove('loading-active');
$ionicBody.removeClass('loading-active');
setTimeout(function() {
!self.isShown && self.element.removeClass('visible');
}, 200);
Expand Down
10 changes: 5 additions & 5 deletions js/angular/service/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@
IonicModule
.factory('$ionicModal', [
'$rootScope',
'$document',
'$ionicBody',
'$compile',
'$timeout',
'$ionicPlatform',
'$ionicTemplateLoader',
'$q',
'$log',
function($rootScope, $document, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $q, $log) {
function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $q, $log) {

/**
* @ngdoc controller
Expand Down Expand Up @@ -124,12 +124,12 @@ function($rootScope, $document, $compile, $timeout, $ionicPlatform, $ionicTempla

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

if(!self.el.parentElement) {
modalEl.addClass(self.animation);
$document[0].body.appendChild(self.el);
$ionicBody.append(self.el);
}

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

return $timeout(function(){
$document[0].body.classList.remove(self.viewType + '-open');
$ionicBody.removeClass(self.viewType + '-open');
self.el.classList.add('hide');
}, self.hideDelay || 500);
},
Expand Down
10 changes: 5 additions & 5 deletions js/angular/service/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ IonicModule
'$q',
'$timeout',
'$rootScope',
'$document',
'$ionicBody',
'$compile',
'$ionicPlatform',
function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $document, $compile, $ionicPlatform) {
function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $ionicBody, $compile, $ionicPlatform) {
//TODO allow this to be configured
var config = {
stackPushDelay: 75
Expand Down Expand Up @@ -278,7 +278,7 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $docume
var popupPromise = $ionicTemplateLoader.compile({
template: POPUP_TPL,
scope: options.scope && options.scope.$new(),
appendTo: $document[0].body
appendTo: $ionicBody.get()
});
var contentPromise = options.templateUrl ?
$ionicTemplateLoader.load(options.templateUrl) :
Expand Down Expand Up @@ -370,7 +370,7 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $docume
.then(function(popup) {
if (!previousPopup) {
//Add popup-open & backdrop if this is first popup
document.body.classList.add('popup-open');
$ionicBody.addClass('popup-open');
$ionicBackdrop.retain();
//only show the backdrop on the first popup
$ionicPopup._backButtonActionDone = $ionicPlatform.registerBackButtonAction(
Expand Down Expand Up @@ -398,7 +398,7 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $docume
previousPopup.show();
} else {
//Remove popup-open & backdrop if this is last popup
document.body.classList.remove('popup-open');
$ionicBody.removeClass('popup-open');
$ionicBackdrop.release();
($ionicPopup._backButtonActionDone || angular.noop)();
}
Expand Down
8 changes: 8 additions & 0 deletions test/unit/angular/controller/sideMenuController.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ describe('$ionicSideMenus controller', function() {
expect(ctrl.getOpenPercentage()).toEqual(-50);
});

it('should add/remove menu-open from the body class', inject(function($document) {
expect($document[0].body.classList.contains('menu-open')).toEqual(false);
ctrl.openPercentage(100);
expect($document[0].body.classList.contains('menu-open')).toEqual(true);
ctrl.openPercentage(0);
expect($document[0].body.classList.contains('menu-open')).toEqual(false);
}));

// Open
it('should toggle left', function() {
ctrl.toggleLeft();
Expand Down
12 changes: 12 additions & 0 deletions test/unit/angular/directive/sideMenu.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ describe('Ionic Angular Side Menu', function() {
expect(sideMenuController.isAsideExposed()).toBe(false);
}));

it('should add/remove "aside-resizing" from the body tag when using activeAsideResizing', inject(function($compile, $rootScope, $document) {
var el = $compile('<ion-side-menus><ion-side-menu></><ion-side-menu-content></ion-side-menu-content></ion-side-menus>')($rootScope.$new());
$rootScope.$apply();
var sideMenuController = el.controller('ionSideMenus');

expect($document[0].body.classList.contains('aside-resizing')).toEqual(false);
sideMenuController.activeAsideResizing(true);
expect($document[0].body.classList.contains('aside-resizing')).toEqual(true);
sideMenuController.activeAsideResizing(false);
expect($document[0].body.classList.contains('aside-resizing')).toEqual(false);
}));

it('should emit $ionicexposeAside', inject(function($compile, $rootScope) {
var el = $compile('<ion-side-menus><ion-side-menu></><ion-side-menu-content></ion-side-menu-content></ion-side-menus>')($rootScope.$new());
$rootScope.$apply();
Expand Down
Loading

0 comments on commit 2c3f1c9

Please sign in to comment.