diff --git a/src/ng/directive/booleanAttrs.js b/src/ng/directive/booleanAttrs.js
index b290d52c0eac..c364604bbcdb 100644
--- a/src/ng/directive/booleanAttrs.js
+++ b/src/ng/directive/booleanAttrs.js
@@ -172,6 +172,44 @@
* then special attribute "disabled" will be set on the element
*/
+ /**
+ * @ngdoc directive
+ * @name ng.directive:ngEnabled
+ * @restrict A
+ *
+ * @description
+ *
+ * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
+ *
+ *
+ *
+ *
+ *
+ *
+ * The HTML specs do not require browsers to preserve the values of special attributes
+ * such as disabled. (The presence of them means true and absence means false)
+ * This prevents the Angular compiler from correctly retrieving the binding expression.
+ * To solve this problem, we introduce the `ngDisabled` directive and its corollary `ngEnabled`.
+ *
+ * @example
+
+
+ Click me to toggle:
+
+
+
+ it('should toggle button', function() {
+ expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
+ input('checked').check();
+ expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
+ });
+
+
+ *
+ * @element INPUT
+ * @param {expression} ngEnabled If the {@link guide/expression expression} is falsy,
+ * then special attribute "disabled" will be set on the element
+ */
/**
* @ngdoc directive
@@ -199,7 +237,7 @@
*
* @element INPUT
- * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
+ * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
* then special attribute "checked" will be set on the element
*/
@@ -230,7 +268,7 @@
*
* @element INPUT
- * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
+ * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
* then special attribute "readonly" will be set on the element
*/
@@ -303,27 +341,34 @@
var ngAttributeAliasDirectives = {};
+function ngAttributeAliasDirective(attrName, directiveName, inverted) {
+ inverted = !!inverted;
-// boolean attrs are evaluated
-forEach(BOOLEAN_ATTR, function(propName, attrName) {
- // binding to multiple is not supported
- if (propName == "multiple") return;
-
- var normalized = directiveNormalize('ng-' + attrName);
- ngAttributeAliasDirectives[normalized] = function() {
+ return function() {
return {
priority: 100,
compile: function() {
return function(scope, element, attr) {
- scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
- attr.$set(attrName, !!value);
+ scope.$watch(attr[directiveName], function ngBooleanAttrWatchAction(value) {
+ attr.$set(attrName, !!(inverted ^ !!value));
});
};
}
};
};
+}
+
+
+// boolean attrs are evaluated
+forEach(BOOLEAN_ATTR, function(propName, attrName) {
+ // binding to multiple is not supported
+ if (propName == "multiple") return;
+
+ var normalized = directiveNormalize('ng-' + attrName);
+ ngAttributeAliasDirectives[normalized] = ngAttributeAliasDirective(attrName, normalized);
});
+ngAttributeAliasDirectives['ngEnabled'] = ngAttributeAliasDirective('disabled', 'ngEnabled', true);
// ng-src, ng-srcset, ng-href are interpolated
forEach(['src', 'srcset', 'href'], function(attrName) {
diff --git a/test/ng/directive/booleanAttrsSpec.js b/test/ng/directive/booleanAttrsSpec.js
index 83bc75e55188..d657c345e5d3 100644
--- a/test/ng/directive/booleanAttrsSpec.js
+++ b/test/ng/directive/booleanAttrsSpec.js
@@ -30,6 +30,16 @@ describe('boolean attr directives', function() {
expect(element.attr('disabled')).toBeTruthy();
}));
+ it('should bind disabled to the negation of ng-enabled', inject(function($rootScope, $compile) {
+ element = $compile('')($rootScope);
+ $rootScope.isEnabled = false;
+ $rootScope.$digest();
+ expect(element.attr('disabled')).toBeTruthy();
+ $rootScope.isEnabled = true;
+ $rootScope.$digest();
+ expect(element.attr('disabled')).toBeFalsy();
+ }));
+
it('should bind checked', inject(function($rootScope, $compile) {
element = $compile('')($rootScope)
diff --git a/test/ngTouch/directive/ngClickSpec.js b/test/ngTouch/directive/ngClickSpec.js
index 0eccc8f3d04b..7f2a5e857da9 100644
--- a/test/ngTouch/directive/ngClickSpec.js
+++ b/test/ngTouch/directive/ngClickSpec.js
@@ -466,6 +466,42 @@ describe('ngClick (touch)', function() {
expect($rootScope.event).toBeDefined();
}));
+ it('should trigger click if ngEnabled is true', inject(function($rootScope, $compile) {
+ element = $compile('')($rootScope);
+ $rootScope.enabled = true;
+ $rootScope.$digest();
+
+ browserTrigger(element, 'touchstart',{
+ keys: [],
+ x: 10,
+ y: 10
+ });
+ browserTrigger(element, 'touchend',{
+ keys: [],
+ x: 10,
+ y: 10
+ });
+
+ expect($rootScope.event).toBeDefined();
+ }));
+ it('should not trigger click if ngEnabled is false', inject(function($rootScope, $compile) {
+ element = $compile('')($rootScope);
+ $rootScope.enabled = false;
+ $rootScope.$digest();
+
+ browserTrigger(element, 'touchstart',{
+ keys: [],
+ x: 10,
+ y: 10
+ });
+ browserTrigger(element, 'touchend',{
+ keys: [],
+ x: 10,
+ y: 10
+ });
+
+ expect($rootScope.event).toBeUndefined();
+ }));
it('should not trigger click if regular disabled is true', inject(function($rootScope, $compile) {
element = $compile('')($rootScope);