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

Commit

Permalink
perf(): use ngDisabled internally instead of disabled
Browse files Browse the repository at this point in the history
Closes #607.

BREAKING CHANGE: For performance, the ng-disabled attribute is now read
to check if a component is disabled instead of the disabled attribute.

If you use the `disabled` attribute on a component to set whether
it is disabled, change it to an ng-disabled expression.

Change your code from this:

```html
<md-checkbox disabled></md-checkbox>
```

To this:

```html
<md-checkboxn ng-disabled="true"></md-checkbox>
```
  • Loading branch information
ajoslin committed Nov 17, 2014
1 parent 42e0d7f commit 2ece8cd
Show file tree
Hide file tree
Showing 18 changed files with 47 additions and 75 deletions.
17 changes: 7 additions & 10 deletions src/components/button/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ angular.module('material.components.button', [
* <md-button href="http://google.com" class="md-button-colored">
* I'm a link
* </md-button>
* <md-button disabled class="md-colored">
* <md-button ng-disabled="true" class="md-colored">
* I'm a disabled button
* </md-button>
* </hljs>
Expand All @@ -58,12 +58,11 @@ function MdButtonDirective($mdInkRipple, $mdTheming, $mdAria) {
}

function getTemplate(element, attr) {
var tag = isAnchor(attr) ? 'a' : 'button';
//We need to manually pass disabled to the replaced element because
//of a bug where it isn't always passed.
var disabled = element[0].hasAttribute('disabled') ? ' disabled ' : ' ';

return '<' + tag + disabled + 'class="md-button" ng-transclude></' + tag + '>';
if (isAnchor(attr)) {
return '<a class="md-button" ng-transclude></a>';
} else {
return '<button class="md-button" ng-transclude></button>';
}
}

function postLink(scope, element, attr) {
Expand All @@ -79,9 +78,7 @@ function MdButtonDirective($mdInkRipple, $mdTheming, $mdAria) {
// For anchor elements, we have to set tabindex manually when the
// element is disabled
if (isAnchor(attr)) {
scope.$watch(function() {
return node.hasAttribute('disabled');
}, function(isDisabled) {
scope.$watch(attr.ngDisabled, function(isDisabled) {
element.attr('tabindex', isDisabled ? -1 : 0);
});
}
Expand Down
6 changes: 0 additions & 6 deletions src/components/button/button.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ describe('md-button', function() {
expect(button[0].tagName.toLowerCase()).toEqual('button');
}));

it('should pass in disabled attribute (testing our DOM bug-fix)', inject(function($compile, $rootScope) {
var button = $compile('<md-button disabled>')($rootScope.$new());
$rootScope.$apply();
expect(button[0].hasAttribute('disabled')).toBe(true);
}));

it('should expect an aria-label if element has no text', inject(function($compile, $rootScope, $log) {
spyOn($log, 'warn');
var button = $compile('<md-button><md-icon></md-icon></md-button>')($rootScope);
Expand Down
4 changes: 2 additions & 2 deletions src/components/button/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<section layout="vertical" layout-sm="horizontal" layout-align="center center">
<md-button class="md-raised">Button</md-button>
<md-button class="md-raised md-primary">Primary</md-button>
<md-button disabled class="md-raised md-primary">Disabled</md-button>
<md-button ng-disabled="true" class="md-raised md-primary">Disabled</md-button>
<md-button class="md-raised md-warn">Warn</md-button>
<div class="label">raised</div>
</section>
Expand All @@ -27,7 +27,7 @@
<md-icon icon="/img/icons/ic_insert_drive_file_24px.svg" style="width: 24px; height: 24px;"></md-icon>
</md-button>

<md-button class="md-fab" disabled aria-label="Comment">
<md-button class="md-fab" ng-disabled="true" aria-label="Comment">
<md-icon icon="/img/icons/ic_comment_24px.svg" style="width: 24px; height: 24px;"></md-icon>
</md-button>

Expand Down
3 changes: 1 addition & 2 deletions src/components/checkbox/checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ angular.module('material.components.checkbox', [
* @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
* @param {string=} ngChange Angular expression to be executed when input changes due to user interaction with the input element.
* @param {boolean=} mdNoInk Use of attribute indicates use of ripple ink effects
* @param {boolean=} disabled Use of attribute indicates the switch is disabled: no ink effects and not selectable
* @param {string=} ariaLabel Publish the button label used by screen-readers for accessibility. Defaults to the checkbox's text.
*
* @usage
Expand All @@ -39,7 +38,7 @@ angular.module('material.components.checkbox', [
* No Ink Effects
* </md-checkbox>
*
* <md-checkbox disabled ng-model="isDisabled" aria-label="Disabled">
* <md-checkbox ng-disabled="true" ng-model="isDisabled" aria-label="Disabled">
* Disabled
* </md-checkbox>
*
Expand Down
5 changes: 3 additions & 2 deletions src/components/checkbox/checkbox.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,19 @@ describe('mdCheckbox', function() {

it('should be disabled with disabled attr', inject(function($compile, $rootScope) {
var element = $compile('<div>' +
'<md-checkbox disabled ng-model="blue">' +
'<md-checkbox ng-disabled="isDisabled" ng-model="blue">' +
'</md-checkbox>' +
'</div>')($rootScope);

var checkbox = element.find('md-checkbox');
$rootScope.$apply('isDisabled = true');

$rootScope.$apply('blue = false');

checkbox.triggerHandler('click');
expect($rootScope.blue).toBe(false);

checkbox.removeAttr('disabled');
$rootScope.$apply('isDisabled = false');

checkbox.triggerHandler('click');
expect($rootScope.blue).toBe(true);
Expand Down
4 changes: 2 additions & 2 deletions src/components/checkbox/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
Checkbox 2: {{ data.cb2 }}
</md-checkbox>

<md-checkbox disabled aria-label="Disabled checkbox">
<md-checkbox ng-disabled="true" aria-label="Disabled checkbox">
Checkbox (Disabled)
</md-checkbox>

<md-checkbox disabled aria-label="Disabled checked checkbox" ng-model="data.cb4" ng-init="data.cb4=true">
<md-checkbox ng-disabled="true" aria-label="Disabled checked checkbox" ng-model="data.cb4" ng-init="data.cb4=true">
Checkbox (Disabled, Checked)
</md-checkbox>

Expand Down
5 changes: 3 additions & 2 deletions src/components/radioButton/radioButton.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,16 @@ describe('radioButton', function() {

it('should be disabled', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="white" disabled></md-radio-button>' +
'<md-radio-button value="white" ng-disabled="isDisabled"></md-radio-button>' +
'</md-radio-group>')($rootScope);
var radio = element.find('md-radio-button');

$rootScope.$apply('isDisabled = true');
$rootScope.$apply('color = null');
radio.triggerHandler('click');
expect($rootScope.color).toBe(null);

radio.removeAttr('disabled');
$rootScope.$apply('isDisabled = false');
radio.triggerHandler('click');
expect($rootScope.color).toBe('white');
}));
Expand Down
8 changes: 4 additions & 4 deletions src/components/slider/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ <h3>Rating: {{rating}}/5</h3>
</md-slider>

<h3>Disabled</h3>
<md-slider ng-model="disabled1" disabled aria-label="Disabled 1"></md-slider>
<md-slider ng-model="disabled2" disabled aria-label="Disabled 2"></md-slider>
<md-slider ng-model="disabled1" ng-disabled="true" aria-label="Disabled 1"></md-slider>
<md-slider ng-model="disabled2" ng-disabled="true" aria-label="Disabled 2"></md-slider>

<h3>Disabled, Discrete</h3>
<md-slider ng-model="disabled1" disabled step="3" md-discrete min="0" max="10" aria-label="Disabled discrete 1"></md-slider>
<md-slider ng-model="disabled2" disabled step="10" md-discrete aria-label="Disabled discrete 2"></md-slider>
<md-slider ng-model="disabled1" ng-disabled="true" step="3" md-discrete min="0" max="10" aria-label="Disabled discrete 1"></md-slider>
<md-slider ng-model="disabled2" ng-disabled="true" step="10" md-discrete aria-label="Disabled discrete 2"></md-slider>

</md-content>
</div>
2 changes: 0 additions & 2 deletions src/components/slider/slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ function SliderController($scope, $element, $attrs, $$rAF, $window, $mdAria, $md
var stopDisabledWatch = angular.noop;
if ($attrs.ngDisabled) {
stopDisabledWatch = $scope.$parent.$watch($attrs.ngDisabled, updateAriaDisabled);
} else {
updateAriaDisabled(!!$attrs.disabled);
}

$mdAria.expect($element, 'aria-label');
Expand Down
3 changes: 0 additions & 3 deletions src/components/sticky/sticky.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@ function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
}

function refreshElements() {
var contentRect = contentEl[0].getBoundingClientRect();


// Sort our collection of elements by their current position in the DOM.
// We need to do this because our elements' order of being added may not
// be the same as their order of display.
Expand Down
4 changes: 2 additions & 2 deletions src/components/switch/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
Switch 2: {{ data.cb2 }}
</md-switch>

<md-switch disabled aria-label="Disabled switch">
<md-switch ng-disabled="true" aria-label="Disabled switch">
Switch (Disabled)
</md-switch>

<md-switch disabled aria-label="Disabled active switch" ng-model="data.cb4" ng-init="data.cb4=true">
<md-switch ng-disabled="true" aria-label="Disabled active switch" ng-model="data.cb4" ng-init="data.cb4=true">
Switch (Disabled, Active)
</md-switch>

Expand Down
9 changes: 1 addition & 8 deletions src/components/switch/switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ angular.module('material.components.switch', [
* @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
* @param {string=} ngChange Angular expression to be executed when input changes due to user interaction with the input element.
* @param {boolean=} mdNoInk Use of attribute indicates use of ripple ink effects.
* @param {boolean=} disabled Use of attribute indicates the switch is disabled: no ink effects and not selectable
* @param {string=} ariaLabel Publish the button label used by screen-readers for accessibility. Defaults to the switch's text.
*
* @usage
Expand All @@ -42,7 +41,7 @@ angular.module('material.components.switch', [
* No Ink Effects
* </md-switch>
*
* <md-switch disabled ng-model="isDisabled" aria-label="Disabled">
* <md-switch ng-disabled="true" ng-model="isDisabled" aria-label="Disabled">
* Disabled
* </md-switch>
*
Expand All @@ -65,17 +64,11 @@ function MdSwitch(mdCheckboxDirective, mdRadioButtonDirective, $mdTheming) {
};

function compile(element, attr) {

var thumb = angular.element(element[0].querySelector('.md-switch-thumb'));
//Copy down disabled attributes for checkboxDirective to use
thumb.attr('disabled', attr.disabled);
thumb.attr('ngDisabled', attr.ngDisabled);

var checkboxLink = checkboxDirective.compile(thumb, attr);

return function (scope, element, attr, ngModelCtrl) {
$mdTheming(element);
var thumb = angular.element(element[0].querySelector('.md-switch-thumb'));
return checkboxLink(scope, thumb, attr, ngModelCtrl);
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/tabs/demoStaticTabs/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div ng-controller="AppCtrl">

<md-tabs md-selected="data.selectedIndex" center>
<md-tabs selected="data.selectedIndex">
<md-tab id="tab1" aria-controls="tab1-content">
Item One
</md-tab>
Expand Down
5 changes: 3 additions & 2 deletions src/components/tabs/js/tabItemController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
angular.module('material.components.tabs')
.controller('$mdTab', TabItemController);

function TabItemController($scope, $element, $compile, $animate, $mdUtil) {
function TabItemController($scope, $element, $attrs, $compile, $animate, $mdUtil, $parse) {
var self = this;

// Properties
Expand All @@ -20,8 +20,9 @@ function TabItemController($scope, $element, $compile, $animate, $mdUtil) {
self.onSelect = onSelect;
self.onDeselect = onDeselect;

var disabledParsed = $parse($attrs.ngDisabled);
function isDisabled() {
return $element[0].hasAttribute('disabled');
return disabledParsed($scope.$parent);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/components/tabs/tabs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,11 @@ describe('<md-tabs>', function() {
tabs.scope().$apply('disabled0 = true');
expect(tabItems.eq(1)).toBeActiveTab();
expect(tabItems.eq(0).attr('aria-disabled')).toBe('true');
expect(tabItems.eq(1).attr('aria-disabled')).toBe('false');
expect(tabItems.eq(1).attr('aria-disabled')).not.toBe('true');

tabs.scope().$apply('disabled0 = false; disabled1 = true');
expect(tabItems.eq(0)).toBeActiveTab();
expect(tabItems.eq(0).attr('aria-disabled')).toBe('false');
expect(tabItems.eq(0).attr('aria-disabled')).not.toBe('true');
expect(tabItems.eq(1).attr('aria-disabled')).toBe('true');
});

Expand Down
36 changes: 13 additions & 23 deletions src/components/textField/textField.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ angular.module('material.components.textField', [
* <md-text-float label="LastName" ng-model="user.lastName" > </md-text-float>
*
* <!-- Specify a read-only input field by using the `disabled` attribute -->
* <md-text-float label="Company" ng-model="user.company" disabled > </md-text-float>
* <md-text-float label="Company" ng-model="user.company" ng-disabled="true" > </md-text-float>
*
* <!-- Specify an input type if desired. -->
* <md-text-float label="eMail" ng-model="user.email" type="email" ></md-text-float>
* </hljs>
*/
function mdTextFloatDirective($mdTheming, $mdUtil) {
function mdTextFloatDirective($mdTheming, $mdUtil, $parse) {
return {
restrict: 'E',
replace: true,
Expand All @@ -59,26 +59,20 @@ function mdTextFloatDirective($mdTheming, $mdUtil) {

return {
pre : function(scope, element, attrs) {
// transpose `disabled` flag
if ( angular.isDefined(attrs.disabled) ) {
element.attr('disabled', true);
scope.isDisabled = true;
}
var disabledParsed = $parse(attrs.ngDisabled);
scope.isDisabled = function() {
return disabledParsed(scope.$parent);
};

scope.inputType = attrs.type || "text";
element.removeAttr('type');

// transpose optional `class` settings
element.attr('class', attrs.class );

},
post: $mdTheming
};
},
template:
'<md-input-group ng-disabled="isDisabled" tabindex="-1">' +
'<md-input-group tabindex="-1">' +
' <label for="{{fid}}" >{{label}}</label>' +
' <md-input id="{{fid}}" ng-model="value" type="{{inputType}}"></md-input>' +
' <md-input id="{{fid}}" ng-disabled="isDisabled()" ng-model="value" type="{{inputType}}"></md-input>' +
'</md-input-group>'
};
}
Expand Down Expand Up @@ -148,15 +142,11 @@ function mdInputDirective($mdUtil) {
var inputGroupCtrl = ctrls[0];
var ngModelCtrl = ctrls[1];

// scan for disabled and transpose the `type` value to the <input> element
var parent = element[0].parentNode;
var isDisabled = parent && parent.hasAttribute('disabled');

element.attr({
'tabindex': isDisabled ? -1 : 0,
'aria-disabled': isDisabled ? 'true' : 'false',
'type': attr.type || element.parent().attr('type') || "text"
scope.$watch(scope.isDisabled, function(isDisabled) {
element.attr('aria-disabled', !!isDisabled);
element.attr('tabindex', !!isDisabled);
});
element.attr('type', attr.type || element.parent().attr('type') || "text");

// When the input value changes, check if it "has" a value, and
// set the appropriate class on the input group
Expand Down Expand Up @@ -191,7 +181,7 @@ function mdInputDirective($mdUtil) {
function isNotEmpty(value) {
value = angular.isUndefined(value) ? element.val() : value;
return (angular.isDefined(value) && (value!==null) &&
(value.toString().trim() != ""));
(value.toString().trim() !== ""));
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/textField/textField.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ describe('Text Field directives', function() {
});

it('should add an ARIA attribute for disabled inputs', function() {
var markup ='<md-text-float disabled ' +
var markup ='<md-text-float ng-disabled="true" ' +
' label="{{labels.firstName}}" ' +
' ng-model="user.firstName" >' +
'</md-text-float>';
Expand Down
3 changes: 2 additions & 1 deletion src/core/services/ripple/ripple.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ function InkRippleService($window, $timeout) {
};

function rippleIsAllowed() {
var parent;
return !element[0].hasAttribute('disabled') &&
!(element[0].parentNode && element[0].parentNode.hasAttribute('disabled'));
!((parent = element[0].parentNode) && parent.hasAttribute('disabled'));
}

function removeElement(element, wait) {
Expand Down

0 comments on commit 2ece8cd

Please sign in to comment.