2
votes

I wrote an Angular directive to capture percentage values in fractions (100% = 1.0) in model, but whole numbers in view. It works great in most of the scenarios, but I am not sure how to trigger the initial validation.

For example, when the user is presented with the form, the default value is zero. When the min and max are set to 1 and 100, the validation doesn't happen.

Can someone let me know how to make it work in an elegant way?

Plunker link: http://plnkr.co/edit/ioraHEK63SuGMncTuHBw?p=info

export function PercentageInputFormatter() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, vm) {
            if (vm) {

                var minNumber: number = 'min' in attrs ? parseFloat(attrs.min) : Number.MIN_VALUE;
                var maxNumber: number= 'max' in attrs ? parseFloat(attrs.max) : Number.MAX_VALUE;
                var allowFraction: boolean = 'fraction' in attrs ? attrs.fraction === 'true' : false;

                vm.$parsers.push(function (value) {
                    return validatePercentage(value);
                });

                vm.$formatters.push(function (value) {
                    return XpressConvertionUtils.toPercentageDisplay(value, allowFraction ? 2 : 0);
                });

                function validatePercentage(value) {
                    if (value) {
                        var valid = !isNaN(parseFloat(value)) && isFinite(value) && parseFloat(value) >= minNumber && parseFloat(value) <= maxNumber;
                        if (!allowFraction)
                            valid = valid && value.indexOf('.') == -1;

                        vm.$setValidity('percentage', valid);
                    }

                    return valid ? value / 100 : undefined;
                }
            }
        }
    };
}

myApp.directive('dpInputFormatterPercentage', PercentageInputFormatter);
1

1 Answers

2
votes

Found a solution, but please feel free to suggest better ways, as I feel this is a work-around and doesn't look very elegant. If not, hope this helps someone else.

This part did the trick:

var modelGetter = $parse(attrs.ngModel);
var initialValue = modelGetter(scope);
validatePercentage($filter('percentage')(initialValue, allowFraction ? 2 : 0));

Full directive code below:

    export function PercentageInputFormatter($parse, $filter) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, vm) {
            if (vm) {

                var minNumber: number = 'min' in attrs ? parseFloat(attrs.min) : Number.MIN_VALUE;
                var maxNumber: number= 'max' in attrs ? parseFloat(attrs.max) : Number.MAX_VALUE;
                var allowFraction: boolean = 'fraction' in attrs ? attrs.fraction === 'true' : false;

                var modelGetter = $parse(attrs.ngModel);
                var initialValue = modelGetter(scope);
                validatePercentage($filter('percentage')(initialValue, allowFraction ? 2 : 0));

                vm.$parsers.push(function (value) {
                    return validatePercentage(value);
                });

                vm.$formatters.push(function (value) {
                    return $filter('percentage')(value, allowFraction ? 2 : 0);
                });

                function validatePercentage(value) {
                    if (value) {
                        var valid = !isNaN(parseFloat(value)) && isFinite(value) && parseFloat(value) >= minNumber && parseFloat(value) <= maxNumber;
                        if (!allowFraction)
                            valid = valid && value.indexOf('.') == -1;

                        vm.$setValidity('percentage', valid);
                    }

                    return valid ? value / 100 : undefined;
                }
            }
        }
    };
}