Skip to content

Commit

Permalink
fix(input): always format viewValue as a string inputs with text cont…
Browse files Browse the repository at this point in the history
…rols

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
  • Loading branch information
caitp committed Sep 25, 2014
1 parent 37f2265 commit 3c80dcf
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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),
Expand Down
69 changes: 69 additions & 0 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<form name="form"><input name="field" ng-model="val" /></form>')($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('<form name="form"><input type="text" name="field" ng-model="val" /></form>')($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('<form name="form"><input type="email" name="field" ng-model="val" /></form>')($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('<form name="form"><input type="url" name="field" ng-model="val" /></form>')($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) {

Expand Down

0 comments on commit 3c80dcf

Please sign in to comment.