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

Commit 09e175f

Browse files
committed
feat(ngValue): allow radio inputs to have non string values
Closes #816
1 parent 5c5b118 commit 09e175f

File tree

5 files changed

+86
-10
lines changed

5 files changed

+86
-10
lines changed

src/AngularPublic.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ function publishExternalAPI(angular){
101101
ngChange: ngChangeDirective,
102102
ngModelInstant: ngModelInstantDirective,
103103
required: requiredDirective,
104-
ngRequired: requiredDirective
104+
ngRequired: requiredDirective,
105+
ngValue: ngValueDirective
105106
}).
106107
directive(ngAttributeAliasDirectives).
107108
directive(ngEventDirectives);

src/directive/input.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ function radioInputType(scope, element, attr, ctrl) {
558558

559559
ctrl.$render = function() {
560560
var value = attr.value;
561-
element[0].checked = isDefined(value) && (value == ctrl.$viewValue);
561+
element[0].checked = (value == ctrl.$viewValue);
562562
};
563563

564564
attr.$observe('value', ctrl.$render);
@@ -1168,3 +1168,27 @@ var ngListDirective = function() {
11681168
}
11691169
};
11701170
};
1171+
1172+
1173+
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
1174+
1175+
var ngValueDirective = [function() {
1176+
return {
1177+
priority: 100,
1178+
compile: function(tpl, attr) {
1179+
if (CONSTANT_VALUE_REGEXP.test(attr.ngValue)) {
1180+
return function(scope) {
1181+
attr.$set('value', scope.$eval(attr.ngValue));
1182+
};
1183+
} else {
1184+
attr.$observers.value = [];
1185+
1186+
return function(scope) {
1187+
scope.$watch(attr.ngValue, function(value) {
1188+
attr.$set('value', value, false);
1189+
});
1190+
};
1191+
}
1192+
}
1193+
};
1194+
}];

src/service/compiler.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ function $CompileProvider($provide) {
732732
if (src[key]) {
733733
value += (key === 'style' ? ';' : ' ') + src[key];
734734
}
735-
dst.$set(key, value, srcAttr[key]);
735+
dst.$set(key, value, true, srcAttr[key]);
736736
}
737737
});
738738
// copy the new attributes on the old attrs object
@@ -937,9 +937,11 @@ function $CompileProvider($provide) {
937937
* can share the attribute. This function properly handles boolean attributes.
938938
* @param {string} key Normalized key. (ie ngAttribute)
939939
* @param {string|boolean} value The value to set. If `null` attribute will be deleted.
940+
* @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
941+
* Defaults to true.
940942
* @param {string=} attrName Optional none normalized name. Defaults to key.
941943
*/
942-
function attrSetter(key, value, attrName) {
944+
function attrSetter(key, value, writeAttr, attrName) {
943945
var booleanKey = isBooleanAttr(this.$element[0], key.toLowerCase());
944946

945947
if (booleanKey) {
@@ -962,12 +964,15 @@ function $CompileProvider($provide) {
962964
}
963965
}
964966

965-
if (value === null || value === undefined) {
966-
this.$element.removeAttr(attrName);
967-
} else {
968-
this.$element.attr(attrName, value);
967+
if (writeAttr !== false) {
968+
if (value === null || value === undefined) {
969+
this.$element.removeAttr(attrName);
970+
} else {
971+
this.$element.attr(attrName, value);
972+
}
969973
}
970974

975+
971976
// fire observers
972977
forEach(this.$observers[key], function(fn) {
973978
try {

test/directive/inputSpec.js

+38
Original file line numberDiff line numberDiff line change
@@ -1078,4 +1078,42 @@ describe('input', function() {
10781078
expect(scope.value).toBe('value3');
10791079
}));
10801080
});
1081+
1082+
1083+
describe('ng-value', function() {
1084+
1085+
it('should evaluate and set constant expressions', function() {
1086+
compileInput('<input type="radio" ng-model="selected" ng-value="true">' +
1087+
'<input type="radio" ng-model="selected" ng-value="false">' +
1088+
'<input type="radio" ng-model="selected" ng-value="1">');
1089+
scope.$digest();
1090+
1091+
browserTrigger(inputElm[0], 'click');
1092+
expect(scope.selected).toBe(true);
1093+
1094+
browserTrigger(inputElm[1], 'click');
1095+
expect(scope.selected).toBe(false);
1096+
1097+
browserTrigger(inputElm[2], 'click');
1098+
expect(scope.selected).toBe(1);
1099+
});
1100+
1101+
1102+
it('should watch the expression', function() {
1103+
compileInput('<input type="radio" ng-model="selected" ng-value="value">');
1104+
1105+
scope.$apply(function() {
1106+
scope.selected = scope.value = {some: 'object'};
1107+
});
1108+
expect(inputElm[0].checked).toBe(true);
1109+
1110+
scope.$apply(function() {
1111+
scope.value = {some: 'other'};
1112+
});
1113+
expect(inputElm[0].checked).toBe(false);
1114+
1115+
browserTrigger(inputElm, 'click');
1116+
expect(scope.selected).toBe(scope.value);
1117+
});
1118+
});
10811119
});

test/service/compilerSpec.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1401,7 +1401,7 @@ describe('$compile', function() {
14011401

14021402

14031403
it('should allow overriding of attribute name and remember the name', function() {
1404-
attr.$set('ngOther', '123', 'other');
1404+
attr.$set('ngOther', '123', true, 'other');
14051405
expect(element.attr('other')).toEqual('123');
14061406
expect(attr.ngOther).toEqual('123');
14071407

@@ -1437,7 +1437,15 @@ describe('$compile', function() {
14371437
attr.$set('ngMyAttr', 'value');
14381438
attr.$set('ngMyAttr', null);
14391439
expect(element.attr('ng-my-attr')).toBe(undefined);
1440-
})
1440+
});
1441+
1442+
1443+
it('should not set DOM element attr if writeAttr false', function() {
1444+
attr.$set('test', 'value', false);
1445+
1446+
expect(element.attr('test')).toBeUndefined();
1447+
expect(attr.test).toBe('value');
1448+
});
14411449
});
14421450
});
14431451

0 commit comments

Comments
 (0)