Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
fix(checkbox): avoid potential memory leaks, general cleanup
Browse files Browse the repository at this point in the history
* Avoids potential memory leaks in the checkbox component by removing any event handlers from the `compile` function and avoiding references between `compile` and `link`.
* Cleans out a few redundancies in the checkbox link function.

Fixes #7993.

Closes #9130
  • Loading branch information
crisbeto authored and ThomasBurleson committed Jul 25, 2016
1 parent 11cbe3a commit 1a2b1a8
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 34 deletions.
60 changes: 27 additions & 33 deletions src/components/checkbox/checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ angular
* @param {expression=} md-indeterminate This determines when the checkbox should be rendered as 'indeterminate'.
* If a truthy expression or no value is passed in the checkbox renders in the md-indeterminate state.
* If falsy expression is passed in it just looks like a normal unchecked checkbox.
* The indeterminate, checked, and unchecked states are mutually exclusive. A box cannot be in any two states at the same time.
* Adding the 'md-indeterminate' attribute overrides any checked/unchecked rendering logic.
* The indeterminate, checked, and unchecked states are mutually exclusive. A box cannot be in any two states at the same time.
* Adding the 'md-indeterminate' attribute overrides any checked/unchecked rendering logic.
* When using the 'md-indeterminate' attribute use 'ng-checked' to define rendering logic instead of using 'ng-model'.
* @param {expression=} ng-checked If this expression evaluates as truthy, the 'md-checked' css class is added to the checkbox and it
* @param {expression=} ng-checked If this expression evaluates as truthy, the 'md-checked' css class is added to the checkbox and it
* will appear checked.
*
* @usage
Expand All @@ -56,7 +56,6 @@ angular
*/
function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $mdUtil, $timeout) {
inputDirective = inputDirective[0];
var CHECKED_CSS = 'md-checked';

return {
restrict: 'E',
Expand All @@ -76,32 +75,35 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $
// **********************************************************

function compile (tElement, tAttrs) {
var container = tElement.children();
var mdIndeterminateStateEnabled = $mdUtil.parseAttributeBoolean(tAttrs.mdIndeterminate);

tAttrs.$set('tabindex', tAttrs.tabindex || '0');
tAttrs.$set('type', 'checkbox');
tAttrs.$set('role', tAttrs.type);

// Attach a click handler in compile in order to immediately stop propagation
// (especially for ng-click) when the checkbox is disabled.
tElement.on('click', function(event) {
if (this.hasAttribute('disabled')) {
event.stopImmediatePropagation();
}
});

// Redirect focus events to the root element, because IE11 is always focusing the container element instead
// of the md-checkbox element. This causes issues when using ngModelOptions: `updateOnBlur`
container.on('focus', function() {
tElement.focus();
});
return {
pre: function(scope, element) {
// Attach a click handler during preLink, in order to immediately stop propagation
// (especially for ng-click) when the checkbox is disabled.
element.on('click', function(e) {
if (this.hasAttribute('disabled')) {
e.stopImmediatePropagation();
}
});
},
post: postLink
};

return function postLink(scope, element, attr, ngModelCtrl) {
function postLink(scope, element, attr, ngModelCtrl) {
var isIndeterminate;
ngModelCtrl = ngModelCtrl || $mdUtil.fakeNgModel();
$mdTheming(element);
if (mdIndeterminateStateEnabled) {

// Redirect focus events to the root element, because IE11 is always focusing the container element instead
// of the md-checkbox element. This causes issues when using ngModelOptions: `updateOnBlur`
element.children().on('focus', function() {
element.focus();
});

if ($mdUtil.parseAttributeBoolean(attr.mdIndeterminate)) {
setIndeterminateState();
scope.$watch(attr.mdIndeterminate, setIndeterminateState);
}
Expand Down Expand Up @@ -162,11 +164,7 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $
var keyCode = ev.which || ev.keyCode;
if (keyCode === $mdConstant.KEY_CODE.SPACE || keyCode === $mdConstant.KEY_CODE.ENTER) {
ev.preventDefault();

if (!element.hasClass('md-focused')) {
element.addClass('md-focused');
}

element.addClass('md-focused');
listener(ev);
}
}
Expand All @@ -182,17 +180,13 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $
// Toggle the checkbox value...
var viewValue = attr.ngChecked ? attr.checked : !ngModelCtrl.$viewValue;

ngModelCtrl.$setViewValue( viewValue, ev && ev.type);
ngModelCtrl.$setViewValue(viewValue, ev && ev.type);
ngModelCtrl.$render();
});
}

function render() {
if(ngModelCtrl.$viewValue && !isIndeterminate) {
element.addClass(CHECKED_CSS);
} else {
element.removeClass(CHECKED_CSS);
}
element.toggleClass('md-checked', ngModelCtrl.$viewValue && !isIndeterminate);
}

function setIndeterminateState(newValue) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/switch/switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function MdSwitch(mdCheckboxDirective, $mdUtil, $mdConstant, $parse, $$rAF, $mdG
};

function mdSwitchCompile(element, attr) {
var checkboxLink = checkboxDirective.compile(element, attr);
var checkboxLink = checkboxDirective.compile(element, attr).post;
// No transition on initial load.
element.addClass('md-dragging');

Expand Down

0 comments on commit 1a2b1a8

Please sign in to comment.