Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feature(ngEnabled): define ngEnabled directive as negation of ngDisabled #4240

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 56 additions & 11 deletions src/ng/directive/booleanAttrs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:
* <pre>
* <div ng-init="scope = { isDisabled: false }">
* <button disabled="{{scope.isDisabled}}">Disabled</button>
* </div>
* </pre>
*
* 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
<doc:example>
<doc:source>
Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
<button ng-model="button" ng-enabled="checked">Button</button>
</doc:source>
<doc:scenario>
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();
});
</doc:scenario>
</doc:example>
*
* @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
Expand Down Expand Up @@ -199,7 +237,7 @@
</doc:example>
*
* @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
*/

Expand Down Expand Up @@ -230,7 +268,7 @@
</doc:example>
*
* @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
*/

Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions test/ng/directive/booleanAttrsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<button ng-enabled="isEnabled">Button</button>')($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('<input type="checkbox" ng-checked="isChecked" />')($rootScope)
Expand Down
36 changes: 36 additions & 0 deletions test/ngTouch/directive/ngClickSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<div ng-click="event = $event" ng-enabled="enabled"></div>')($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('<div ng-click="event = $event" ng-enabled="enabled"></div>')($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('<div ng-click="event = $event" disabled="true"></div>')($rootScope);

Expand Down