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) {