Skip to content

Commit 3c80dcf

Browse files
committed
fix(input): always format viewValue as a string inputs with text controls
Backported from 1eda183 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 angular#5936
1 parent 37f2265 commit 3c80dcf

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

src/ng/directive/input.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,18 @@ function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, va
475475
}
476476
}
477477

478-
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
478+
function stringBasedInputType(ctrl) {
479+
ctrl.$formatters.push(function stringifier(value) {
480+
return ctrl.$isEmpty(value) ? value : value.toString();
481+
});
482+
}
483+
484+
function textInputType(scope, element, attr,ctrl, $sniffer, $browser) {
485+
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
486+
stringBasedInputType(ctrl);
487+
}
488+
489+
function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
479490
var validity = element.prop(VALIDITY_STATE_PROPERTY);
480491
var placeholder = element[0].placeholder, noevent = {};
481492
var type = lowercase(element[0].type);
@@ -1535,6 +1546,8 @@ var requiredDirective = function() {
15351546
*/
15361547
var ngListDirective = function() {
15371548
return {
1549+
restrict: 'A',
1550+
priority: 100,
15381551
require: 'ngModel',
15391552
link: function(scope, element, attr, ctrl) {
15401553
var match = /\/(.*)\//.exec(attr.ngList),

test/ng/directive/inputSpec.js

+69
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,75 @@ describe('ngModel', function() {
310310
}));
311311

312312

313+
it('should always format the viewValue as a string for a blank input type when the value is present',
314+
inject(function($compile, $rootScope, $sniffer) {
315+
316+
var form = $compile('<form name="form"><input name="field" ng-model="val" /></form>')($rootScope);
317+
318+
$rootScope.val = 123;
319+
$rootScope.$digest();
320+
expect($rootScope.form.field.$viewValue).toBe('123');
321+
322+
$rootScope.val = null;
323+
$rootScope.$digest();
324+
expect($rootScope.form.field.$viewValue).toBe(null);
325+
326+
dealoc(form);
327+
}));
328+
329+
330+
it('should always format the viewValue as a string for a `text` input type when the value is present',
331+
inject(function($compile, $rootScope, $sniffer) {
332+
333+
var form = $compile('<form name="form"><input type="text" name="field" ng-model="val" /></form>')($rootScope);
334+
$rootScope.val = 123;
335+
$rootScope.$digest();
336+
expect($rootScope.form.field.$viewValue).toBe('123');
337+
338+
$rootScope.val = null;
339+
$rootScope.$digest();
340+
expect($rootScope.form.field.$viewValue).toBe(null);
341+
342+
dealoc(form);
343+
}));
344+
345+
346+
it('should always format the viewValue as a string for an `email` input type when the value is present',
347+
inject(function($compile, $rootScope, $sniffer) {
348+
349+
var fakeEmail = {};
350+
fakeEmail.toString = function() { return 'fake@email'; };
351+
var form = $compile('<form name="form"><input type="email" name="field" ng-model="val" /></form>')($rootScope);
352+
$rootScope.val = fakeEmail;
353+
$rootScope.$digest();
354+
expect($rootScope.form.field.$viewValue).toBe('fake@email');
355+
356+
$rootScope.val = null;
357+
$rootScope.$digest();
358+
expect($rootScope.form.field.$viewValue).toBe(null);
359+
360+
dealoc(form);
361+
}));
362+
363+
364+
it('should always format the viewValue as a string for a `url` input type when the value is present',
365+
inject(function($compile, $rootScope, $sniffer) {
366+
367+
var fakeUrl = {};
368+
fakeUrl.toString = function() { return 'https://www.angularjs.org'; };
369+
var form = $compile('<form name="form"><input type="url" name="field" ng-model="val" /></form>')($rootScope);
370+
$rootScope.val = fakeUrl;
371+
$rootScope.$digest();
372+
expect($rootScope.form.field.$viewValue).toBe('https://www.angularjs.org');
373+
374+
$rootScope.val = null;
375+
$rootScope.$digest();
376+
expect($rootScope.form.field.$viewValue).toBe(null);
377+
378+
dealoc(form);
379+
}));
380+
381+
313382
it('should register/deregister a nested ngModel with parent form when entering or leaving DOM',
314383
inject(function($compile, $rootScope) {
315384

0 commit comments

Comments
 (0)