From 3c80dcff6efe8b4188a6e3fd175ca745ea11a812 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Thu, 25 Sep 2014 13:08:10 -0400 Subject: [PATCH] fix(input): always format viewValue as a string inputs with text controls Backported from 1eda18365a348c9597aafba9d195d345e4f13d1e NgModel will format all scope-based values to string when setting the viewValue for the associated input element. The formatting, however, only applies to input elements that contain a text, email, url or blank input type. In the event of a null or undefined scope or model value, the viewValue will be set to null or undefined instead of being converted to an empty string. Closes #5936 --- src/ng/directive/input.js | 15 +++++++- test/ng/directive/inputSpec.js | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 98973ae5aa4a..fb3bc667230c 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -475,7 +475,18 @@ function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, va } } -function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { +function stringBasedInputType(ctrl) { + ctrl.$formatters.push(function stringifier(value) { + return ctrl.$isEmpty(value) ? value : value.toString(); + }); +} + +function textInputType(scope, element, attr,ctrl, $sniffer, $browser) { + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); +} + +function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { var validity = element.prop(VALIDITY_STATE_PROPERTY); var placeholder = element[0].placeholder, noevent = {}; var type = lowercase(element[0].type); @@ -1535,6 +1546,8 @@ var requiredDirective = function() { */ var ngListDirective = function() { return { + restrict: 'A', + priority: 100, require: 'ngModel', link: function(scope, element, attr, ctrl) { var match = /\/(.*)\//.exec(attr.ngList), diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 9af76c9eaaaf..52e85f169ad7 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -310,6 +310,75 @@ describe('ngModel', function() { })); + it('should always format the viewValue as a string for a blank input type when the value is present', + inject(function($compile, $rootScope, $sniffer) { + + var form = $compile('
')($rootScope); + + $rootScope.val = 123; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe('123'); + + $rootScope.val = null; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe(null); + + dealoc(form); + })); + + + it('should always format the viewValue as a string for a `text` input type when the value is present', + inject(function($compile, $rootScope, $sniffer) { + + var form = $compile('
')($rootScope); + $rootScope.val = 123; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe('123'); + + $rootScope.val = null; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe(null); + + dealoc(form); + })); + + + it('should always format the viewValue as a string for an `email` input type when the value is present', + inject(function($compile, $rootScope, $sniffer) { + + var fakeEmail = {}; + fakeEmail.toString = function() { return 'fake@email'; }; + var form = $compile('
')($rootScope); + $rootScope.val = fakeEmail; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe('fake@email'); + + $rootScope.val = null; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe(null); + + dealoc(form); + })); + + + it('should always format the viewValue as a string for a `url` input type when the value is present', + inject(function($compile, $rootScope, $sniffer) { + + var fakeUrl = {}; + fakeUrl.toString = function() { return 'https://www.angularjs.org'; }; + var form = $compile('
')($rootScope); + $rootScope.val = fakeUrl; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe('https://www.angularjs.org'); + + $rootScope.val = null; + $rootScope.$digest(); + expect($rootScope.form.field.$viewValue).toBe(null); + + dealoc(form); + })); + + it('should register/deregister a nested ngModel with parent form when entering or leaving DOM', inject(function($compile, $rootScope) {