Eric B.
Eric B.

Reputation: 24411

input[number] field does not display string value while binding in angular

My form data is returned from a REST call. Example data is:

{
    id: 4
    version: 3
    code: "ADSFASDF"
    definition: "asdflkj"
    value: "1234"
    active: "false"
    formula: false
    validTo: "2014-12-31T05:00:00"
    validFrom: "2010-12-31T10:00:00"
}

I'm running into trouble using input[number] and input[date] fields as they require the data to be a number or a date respectively and not a string. Similar problems occur for booleans (check boxes), etc.

I thought I could get around it using a $formattter, but the value passed to the formatter is always "". I presume this means the that $formatter is called after Angular has already tried to parse the data model.

Without having to initially cast everything in my controller, is there a way to cast the data via a directive, or within the HTML tag directly?

Ex:

<input type="number" class="form-control" ng-model="charge.value" jk-numeric id="chargeValue"/>

$formatter:

app.directive( 'jkNumeric',
    function() {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function(scope, element, attrs, ngModelCtrl) {
                var stringToNum = function(text) {
                    if( angular.isNumber(text) )
                        return text;
                    else
                        return parseInt( text, 10);
                }

                // assign the parser and the formatter to the model
                ngModelCtrl.$formatters.unshift(stringToNum);
            }
        };
    });

Upvotes: 0

Views: 1492

Answers (2)

PSL
PSL

Reputation: 123739

You can do this way :-

Set your directive to a higher priority so that the directive runs before ng-model does its viewValue/modelValue evaluation, and do a 2 way binding with ngModel to get the actual value you set (and not the viewValue of ngModel), and to support async data assignment keep a temporary watch. You cannot possibly do it with formatters because they run after ngModel has evaluated the value.

.directive( 'jkNumeric',
    function() {
        return {
            restrict: 'A',
            require: 'ngModel',
            priority:999999, //<-- Give a higher priority
            scope: {         
               model:'=ngModel' //<-- A 2 way binding to read actual bound value not the ngModel view value
            },
            link: function(scope, element, attrs, ngModelCtrl) {

             //Perform whatever formatting you want to do.
             function stringToNum(text) {
                  return +text;
              }

             var unwatch = scope.$watch('model', function(val){
                if(!val) return;
                ngModelCtrl.$setViewValue(stringToNum(val));
                ngModelCtrl.$render();
                unwatch();
              });

            }
        };
    });

Plnkr

Another way to watch would be to watch on evaluated value of ngModel attribute.

            require: 'ngModel',
            priority:999999,
            link: function(scope, element, attrs, ngModelCtrl) {

              function formatToNumber(text) {
                  //do something and return numeric value
              }

             scope.$watch(function(){
                 return scope.$eval(attrs.ngModel); //evaluate the scope value
             }, function(val){
                 if(!val) return;
                 ngModelCtrl.$setViewValue(formatToNumber(val));
                 ngModelCtrl.$render();
              });

Plnk2

Upvotes: 2

apairet
apairet

Reputation: 3172

Could you try something like:

app.directive( 'jkNumeric',
    function() {
        return {
            restrict: 'A',
            require: 'ngModel',
            link: function(scope, element, attrs, ngModelCtrl) {
                if (angular.isString(ngModelCtrl.$viewValue) {
                    ngModelCtrl.$setViewValue(parseInt(ngModelCtrl.$viewValue, 10));
                    ngModelCtrl.$render();
                }
            }
        };
    });

Upvotes: 1

Related Questions