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

Commit 1eda183

Browse files
committed
fix(ngModel): always format the viewValue as a string for text, url and email types
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.
1 parent 77ce5b8 commit 1eda183

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

src/ng/directive/input.js

+18-4
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,18 @@ function testFlags(validity, flags) {
899899
return false;
900900
}
901901

902+
function stringBasedInputType(ctrl) {
903+
ctrl.$formatters.push(function(value) {
904+
return ctrl.$isEmpty(value) ? value : value.toString();
905+
});
906+
}
907+
902908
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
909+
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
910+
stringBasedInputType(ctrl);
911+
}
912+
913+
function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
903914
var validity = element.prop(VALIDITY_STATE_PROPERTY);
904915
var placeholder = element[0].placeholder, noevent = {};
905916
var type = lowercase(element[0].type);
@@ -1050,7 +1061,7 @@ function createDateParser(regexp, mapping) {
10501061
function createDateInputType(type, regexp, parseDate, format) {
10511062
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
10521063
badInputChecker(scope, element, attr, ctrl);
1053-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1064+
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
10541065
var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
10551066

10561067
ctrl.$$parserName = type;
@@ -1100,7 +1111,7 @@ function badInputChecker(scope, element, attr, ctrl) {
11001111

11011112
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11021113
badInputChecker(scope, element, attr, ctrl);
1103-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1114+
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
11041115

11051116
ctrl.$$parserName = 'number';
11061117
ctrl.$parsers.push(function(value) {
@@ -1134,7 +1145,8 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11341145

11351146
function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11361147
badInputChecker(scope, element, attr, ctrl);
1137-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1148+
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
1149+
stringBasedInputType(ctrl);
11381150

11391151
ctrl.$$parserName = 'url';
11401152
ctrl.$validators.url = function(modelValue, viewValue) {
@@ -1145,7 +1157,8 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11451157

11461158
function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11471159
badInputChecker(scope, element, attr, ctrl);
1148-
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
1160+
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
1161+
stringBasedInputType(ctrl);
11491162

11501163
ctrl.$$parserName = 'email';
11511164
ctrl.$validators.email = function(modelValue, viewValue) {
@@ -2585,6 +2598,7 @@ var minlengthDirective = function() {
25852598
var ngListDirective = function() {
25862599
return {
25872600
restrict: 'A',
2601+
priority: 100,
25882602
require: 'ngModel',
25892603
link: function(scope, element, attr, ctrl) {
25902604
// We want to control whitespace trimming so we use this convoluted approach

test/ng/directive/inputSpec.js

+60
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,66 @@ describe('ngModel', function() {
783783
dealoc(element);
784784
}));
785785

786+
it('should always format the viewValue as a string for a blank input type when the value is present',
787+
inject(function($compile, $rootScope, $sniffer) {
788+
789+
var form = $compile('<form name="form"><input name="field" ng-model="val" /></form>')($rootScope);
790+
791+
$rootScope.val = 123;
792+
$rootScope.$digest();
793+
expect($rootScope.form.field.$viewValue).toBe('123');
794+
795+
$rootScope.val = null;
796+
$rootScope.$digest();
797+
expect($rootScope.form.field.$viewValue).toBe(null);
798+
799+
dealoc(form);
800+
}));
801+
802+
it('should always format the viewValue as a string for a `text` input type when the value is present',
803+
inject(function($compile, $rootScope, $sniffer) {
804+
805+
var form = $compile('<form name="form"><input type="text" name="field" ng-model="val" /></form>')($rootScope);
806+
$rootScope.val = 123;
807+
$rootScope.$digest();
808+
expect($rootScope.form.field.$viewValue).toBe('123');
809+
810+
$rootScope.val = null;
811+
$rootScope.$digest();
812+
expect($rootScope.form.field.$viewValue).toBe(null);
813+
814+
dealoc(form);
815+
}));
816+
817+
it('should always format the viewValue as a string for an `email` input type when the value is present',
818+
inject(function($compile, $rootScope, $sniffer) {
819+
820+
var form = $compile('<form name="form"><input type="email" name="field" ng-model="val" /></form>')($rootScope);
821+
$rootScope.val = 123;
822+
$rootScope.$digest();
823+
expect($rootScope.form.field.$viewValue).toBe('123');
824+
825+
$rootScope.val = null;
826+
$rootScope.$digest();
827+
expect($rootScope.form.field.$viewValue).toBe(null);
828+
829+
dealoc(form);
830+
}));
831+
832+
it('should always format the viewValue as a string for a `url` input type when the value is present',
833+
inject(function($compile, $rootScope, $sniffer) {
834+
835+
var form = $compile('<form name="form"><input type="url" name="field" ng-model="val" /></form>')($rootScope);
836+
$rootScope.val = 123;
837+
$rootScope.$digest();
838+
expect($rootScope.form.field.$viewValue).toBe('123');
839+
840+
$rootScope.val = null;
841+
$rootScope.$digest();
842+
expect($rootScope.form.field.$viewValue).toBe(null);
843+
844+
dealoc(form);
845+
}));
786846

787847
it('should set the control touched state on "blur" event', inject(function($compile, $rootScope) {
788848
var element = $compile('<form name="myForm">' +

0 commit comments

Comments
 (0)