Ritesh Kumar Gupta
Ritesh Kumar Gupta

Reputation: 5191

Angular JS- Spinner(number stepper) UI widget directive

I am working on an angular directive for number stepper. Here is the directive:

.directive('rnStepper', function() {
    return {
        restrict: 'AE',
        require: 'ngModel',
        scope: {
            min: '=',
            max: '='
        },
        template: '<div class="spinner-buttons input-group-btn"><button ng-click="decrement();" class="btn spinner-down" type="button">- </button></div>' +
        '<input type="text" style="text-align: center;" maxlength="3" class="spinner-input form-control ng-pristine ng-untouched ng-valid ng-valid-maxlength"/>' +
        '<div class="spinner-buttons input-group-btn"><button ng-click="increment();" class="btn spinner-down" type="button">+</button></div>',
        link: function(scope, iElement, iAttrs, ngModelController) {

            ngModelController.$render = function() {
                iElement.find('input').val(ngModelController.$viewValue);
                // update the validation status
                checkValidity();
            };

            // when model change, cast to integer
            ngModelController.$formatters.push(function(value) {
                return parseInt(value, 10);
            });

            // when view change, cast to integer
            ngModelController.$parsers.push(function(value) {
                return parseInt(value, 10);
            });

            function checkValidity() {
                // check if min/max defined to check validity
                var valid = !(scope.isOverMin(true) || scope.isOverMax(true));
                // set our model validity
                // the outOfBounds is an arbitrary key for the error.
                // will be used to generate the CSS class names for the errors
                ngModelController.$setValidity('outOfBounds', valid);
            }

            function updateModel(offset) {
                // update the model, call $parsers pipeline...
                ngModelController.$setViewValue(ngModelController.$viewValue + offset);
                // update the local view
                ngModelController.$render();
            }

            scope.isOverMin = function(strict) {
                var offset = strict?0:1;
                return (angular.isDefined(scope.min) && (ngModelController.$viewValue - offset) < parseInt(scope.min, 10));
            };
            scope.isOverMax = function(strict) {
                var offset = strict?0:1;
                return (angular.isDefined(scope.max) && (ngModelController.$viewValue + offset) > parseInt(scope.max, 10));
            };


            // update the value when user clicks the buttons
            scope.increment = function() {
                updateModel(+1);
            };
            scope.decrement = function() {
                updateModel(-1);
            };

            // check validity on start, in case we're directly out of bounds
            checkValidity();

            // watch out min/max and recheck validity when they change
            scope.$watch('min+max', function() {
                checkValidity();
            });
        }
    };
});

The '+' and '-' seems to be working fine. However, whenever you modify the value in input text box, then model does not seems to be updated. After modifying value in input text box, whenever you try to increase and decrease then it does not work as expected.

Here is the codepen.

UPD:

As per @Michael answer, I used a ngModel on input text box inside directive.

'<input ng-model="ngModel" type="text" style="text-align: center;"  class="spinner-input form-control"/>' +

The model is updated, however ng-change is not being triggered.

However on clicking + or '-', the ng-change is being triggered whenever I edit the input text box!!

Here is my updated codepen

Upvotes: 0

Views: 2613

Answers (1)

Michael
Michael

Reputation: 3104

The text inputfield doesn't have a binding, so angular won't recognice if you enter a number directly. You need to add a change listener or simply use a ng-model directive.

Upvotes: 1

Related Questions