diff --git a/docs/content/error/ngModel/constexpr.ngdoc b/docs/content/error/ngModel/constexpr.ngdoc new file mode 100644 index 000000000000..7196d1201b28 --- /dev/null +++ b/docs/content/error/ngModel/constexpr.ngdoc @@ -0,0 +1,21 @@ +@ngdoc error +@name ngModel:constexpr +@fullName Non-Constant Expression +@description + +Some attributes used in conjunction with ngModel (such as ngTrueValue or ngFalseValue) will only +accept constant expressions. + +Examples using constant expressions include: + +``` + + +``` + +Examples of non-constant expressions include: + +``` + + +``` diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 9ee879dc1890..226a0a05ca21 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -807,8 +807,8 @@ var inputType = { * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngTrueValue The value to which the expression should be set when selected. - * @param {string=} ngFalseValue The value to which the expression should be set when not selected. + * @param {expression=} ngTrueValue The value to which the expression should be set when selected. + * @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. * @@ -824,7 +824,7 @@ var inputType = {
Value1:
Value2:
+ ng-true-value="'YES'" ng-false-value="'NO'">
value1 = {{value1}}
value2 = {{value2}}
@@ -1183,12 +1183,22 @@ function radioInputType(scope, element, attr, ctrl) { attr.$observe('value', ctrl.$render); } -function checkboxInputType(scope, element, attr, ctrl) { - var trueValue = attr.ngTrueValue, - falseValue = attr.ngFalseValue; +function parseConstantExpr($parse, context, name, expression, fallback) { + var parseFn; + if (isDefined(expression)) { + parseFn = $parse(expression); + if (!parseFn.constant) { + throw new minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' + + '`{1}`.', name, expression); + } + return parseFn(context); + } + return fallback; +} - if (!isString(trueValue)) trueValue = true; - if (!isString(falseValue)) falseValue = false; +function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { + var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true); + var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false); var listener = function(ev) { scope.$apply(function() { @@ -1208,7 +1218,7 @@ function checkboxInputType(scope, element, attr, ctrl) { }; ctrl.$formatters.push(function(value) { - return value === trueValue; + return equals(value, trueValue); }); ctrl.$parsers.push(function(value) { @@ -1356,14 +1366,15 @@ function checkboxInputType(scope, element, attr, ctrl) { */ -var inputDirective = ['$browser', '$sniffer', '$filter', function($browser, $sniffer, $filter) { +var inputDirective = ['$browser', '$sniffer', '$filter', '$parse', + function($browser, $sniffer, $filter, $parse) { return { restrict: 'E', require: ['?ngModel'], link: function(scope, element, attr, ctrls) { if (ctrls[0]) { (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer, - $browser, $filter); + $browser, $filter, $parse); } } }; diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index c4acdc05a679..841a8d434a3b 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -2528,8 +2528,8 @@ describe('input', function() { it('should allow custom enumeration', function() { - compileInput(''); + compileInput(''); scope.$apply(function() { scope.name = 'y'; @@ -2554,6 +2554,27 @@ describe('input', function() { }); + it('should throw if ngTrueValue is present and not a constant expression', function() { + expect(function() { + compileInput(''); + }).toThrowMinErr('ngModel', 'constexpr', "Expected constant expression for `ngTrueValue`, but saw `yes`."); + }); + + + it('should throw if ngFalseValue is present and not a constant expression', function() { + expect(function() { + compileInput(''); + }).toThrowMinErr('ngModel', 'constexpr', "Expected constant expression for `ngFalseValue`, but saw `no`."); + }); + + + it('should not throw if ngTrueValue or ngFalseValue are not present', function() { + expect(function() { + compileInput(''); + }).not.toThrow(); + }); + + it('should be required if false', function() { compileInput('');