2

here on stackoverflow I found a directive made by an user for angularJS, for a input number spinner, and I was just looking for it!

All working good, but one thing is not, when you manually enter a number in the input field, and press MINUS, it s gonna subtract 1 from that number, all good here,

but when you input a number and as first thing you press PLUS, instead of adding 1, it's gonna add a "1" next to the number you wrote! Only going "minus" once fixes it,

can someone help me fix the code to avoid this? I want that when I put the number manually, you can press on "plus" and get +1 on that number!

http://jsfiddle.net/Legendary/84qxvgm8/

4
  • not quite adding 1, but concatenating it. E.g 1+1 gives 2, whereas "1"+1 gives "11". Minus doesn't have that property, so "11"-1 gives 10. A hack / quick fix for it is to cast a Number: scope.ngModel = Number(scope.ngModel) + 1; Commented May 2, 2018 at 11:59
  • you are always on point @AlekseySolovey ! That worked like a charm! thank you very much, write it down as answerand I ll mark it as the one! Commented May 2, 2018 at 12:10
  • can I ask you one more thing, I was trying to edit the code to only accept POSITIVE numbers, I tried using this regex: '/^\d*[1-9]\d*$/' but what happens in that if I press the minus while 0 is displayed, it s gonna return an empty input field, and I can't anymore use "plus" or "minus" until I manually insert a new positive number, can you help me with this? Commented May 2, 2018 at 14:40
  • honestly, the code is a mess, ng-pattern deletes the value if it doesn't fit the regex expression, leaving it as undefined. Casting Number() only partially solves the issue. You can type such values as 1.2.3, which bypasses the pattern as a valid number, but after casting a Number on it or doing +1 or -1 operations, the result is obviously NaN Commented May 2, 2018 at 14:54

2 Answers 2

1

That's because input type="text" will return a string as a model value, and as was mentioned by @AlekseySolovey - you'll have to cast it to a Number (since "10" + 1 will give you "101" as a result). As you are using numericOnly directive as well, it seems to be the right place to do the conversion, since you'll wave to do it only in the one place. Here is an example:

(function () {
    "use strict";

    var app = angular
        .module("app", []);

    app.controller('AppController', ['$scope', function ($scope) {
        var vm = this;

        vm.testNumber = 10;
    }]);

    app.directive('numericOnly', function () {
        return {
            require: 'ngModel',
            link: function (scope, element, attrs, modelCtrl) {

                modelCtrl.$parsers.push(function (inputValue) {
                    if (angular.isNumber(inputValue)) {
                        return inputValue;
                    }

                    var transformedInput = inputValue ? Number(inputValue.replace(/[^\d.-]/g, '')) : null;

                    if (transformedInput != inputValue) {
                        modelCtrl.$setViewValue(transformedInput);
                        modelCtrl.$render();
                    }

                    return transformedInput;
                });
            }
        };
    });

    app.directive('numberSpin', [function () {

        return {
            restrict: 'E',
            scope: {
                "ngModel": '='
            },
            template: '<div>' +
            '<input numeric-only data-ng-model="ngModel" ng-pattern="onlyNumbers" type="text">' +
            '<a class="ns-plus"  data-ng-click="plus()">+</a>' +
            '<a class="ns-minus"data-ng-click="minus()">-</a> </div>',
            link: function (scope, elem, attrs) {

                scope.onlyNumbers = /^\d+$/;

                scope.plus = function () {
                    scope.ngModel = scope.ngModel + 1;
                };

                scope.minus = function () {
                    scope.ngModel = scope.ngModel - 1;
                };

            }
        }

    }])


}());
number-spin div {
  position: relative;
  width: 126px;
  
}
number-spin input {
  height: 32px;
  width: 100%;
  text-align: right;
  padding-right: 20px;
  box-sizing: border-box;
  font-size: 16px;
}

number-spin .ns-plus {
  position: absolute;
  text-align: center;
  line-height: 16px;
  top: 0;
  right: 0;
  height: 16px;
  display: block;
  border-left: 1px solid #ccc;
  border-bottom: 1px solid #ccc;
  width: 16px;
}

number-spin .ns-minus {
  position: absolute;
  text-align: center;
  display: block;
  line-height: 16px;
  height: 16px;
  border-left: 1px solid #ccc;
  bottom: 0;
  right: 0;
  width: 16px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<div ng-app="app" style="padding: 16px; "data-ng-controller="AppController as vm">


<number-spin data-ng-model="vm.testNumber"></number-spin>

</div>

UPDATE: A bit simpler code that allows passing min/max into the directive:

(function () {
    "use strict";

    var app = angular
        .module("app", []);

    app.controller('AppController', ['$scope', function ($scope) {
        var vm = this;

        vm.testNumber = 10;
    }]);

    app.directive('numberSpin', [function () {

        return {
            restrict: 'E',
            scope: {
                "ngModel": '=',
                "min": '<',
                "max": '<',
                "step": '<'
            },
            template: '<div>' +
            '<input data-ng-model="ngModel" type="number" ng-attr-min="{{min}}" ng-attr-max="{{max}}">' +
            '<a class="btn ns-plus" data-ng-click="plus()">+</a>' +
            '<a class="btn ns-minus"data-ng-click="minus()">-</a> </div>',
            link: function (scope, elem, attrs) {

                scope.plus = function () {
                    if (scope.ngModel >= scope.max) return;
                    scope.ngModel += (scope.step || 1);
                    checkModel()
                };

                scope.minus = function () {
                    if (scope.ngModel <= scope.min) return;
                    scope.ngModel -= (scope.step || 1);
                    checkModel();
                };

                function checkModel() {
                    if (!scope.ngModel) scope.ngModel = scope.min || 0;
                }

            }
        }

    }])


}());
number-spin div {
    position: relative;
    width: 126px;

}

number-spin input {
    height: 32px;
    width: 100%;
    text-align: right;
    padding-right: 20px;
    box-sizing: border-box;
    font-size: 16px;
}

number-spin .btn {
    position: absolute;
    text-align: center;
    line-height: 16px;
    display: block;
    height: 16px;
    right: 0;
    border-left: 1px solid #ccc;
    width: 16px;
    cursor: pointer;
    user-select: none;
}

number-spin .ns-plus {
    top: 0;
    border-bottom: 1px solid #ccc;
    cursor: pointer;
    user-select: none;
}

number-spin .ns-minus {
    bottom: 0;
}

number-spin input[type=number]::-webkit-inner-spin-button,
number-spin input[type=number]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<div ng-app="app" style="padding: 16px; "data-ng-controller="AppController as vm">


<number-spin data-ng-model="vm.testNumber" min="0" max="15"></number-spin>

</div>

Sign up to request clarification or add additional context in comments.

1 Comment

can I ask you one more thing, I was trying to edit the code to only accept POSITIVE numbers, I tried using this regex: '/^\d*[1-9]\d*$/' but what happens in that if I press the minus while 0 is displayed, it s gonna return an empty input field, and I can't anymore use "plus" or "minus" until I manually insert a new positive number, can you help me with this?
0

(function () {

    "use strict";

    var app = angular.module("app", ['tien.stepInput']);

    app.controller('AppController', function ($scope) {
       
       $scope.demo = 10
       $scope.options = {
          min_value: 1,
          overrides: [
              {value: 1, style: 'danger', view_value: '<i class="fa fa-times"></i>'},
              {value: 0, style: 'warning', view_value: '<i class="fa fa-question"></i>'}
          ]
      };
       
    });

}());
.tien-step {
  display: inline-block;
  height: 24px;
  width: 93px; }
  .tien-step button, .tien-step .step-value {
    border: none;
    float: left;
    height: 24px;
    text-align: center; }
    .tien-step button:focus, .tien-step button:active, .tien-step button:focus:active, .tien-step .step-value:focus, .tien-step .step-value:active, .tien-step .step-value:focus:active {
      outline: none; }
  .tien-step button {
    background: transparent;
    color: white;
    line-height: 24px;
    width: 24px; }
  .tien-step .step-value {
    border-bottom: 1px solid transparent;
    border-top: 1px solid transparent;
    font-size: 18px;
    line-heigth: 22px;
    padding: 0;
    width: 45px; }
  .tien-step.primary button {
    background: #428bca; }
    .tien-step.primary button:active {
      background: #3071a9; }
    .tien-step.primary button.disabled {
      opacity: 0.8; }
      .tien-step.primary button.disabled:active {
        background: #428bca; }
  .tien-step.primary .step-value {
    border-color: #428bca; }
    .tien-step.primary .step-value i.fa {
      color: #428bca;
      font-size: 14px;
      line-height: 20px;
      top: -2px;
      position: relative; }
  .tien-step.success button {
    background: #5cb85c; }
    .tien-step.success button:active {
      background: #449d44; }
    .tien-step.success button.disabled {
      opacity: 0.8; }
      .tien-step.success button.disabled:active {
        background: #5cb85c; }
  .tien-step.success .step-value {
    border-color: #5cb85c; }
    .tien-step.success .step-value i.fa {
      color: #5cb85c;
      font-size: 14px;
      line-height: 20px;
      top: -2px;
      position: relative; }
  .tien-step.info button {
    background: #5bc0de; }
    .tien-step.info button:active {
      background: #31b0d5; }
    .tien-step.info button.disabled {
      opacity: 0.8; }
      .tien-step.info button.disabled:active {
        background: #5bc0de; }
  .tien-step.info .step-value {
    border-color: #5bc0de; }
    .tien-step.info .step-value i.fa {
      color: #5bc0de;
      font-size: 14px;
      line-height: 20px;
      top: -2px;
      position: relative; }
  .tien-step.warning button {
    background: #f0ad4e; }
    .tien-step.warning button:active {
      background: #ec971f; }
    .tien-step.warning button.disabled {
      opacity: 0.8; }
      .tien-step.warning button.disabled:active {
        background: #f0ad4e; }
  .tien-step.warning .step-value {
    border-color: #f0ad4e; }
    .tien-step.warning .step-value i.fa {
      color: #f0ad4e;
      font-size: 14px;
      line-height: 20px;
      top: -2px;
      position: relative; }
  .tien-step.danger button {
    background: #d9534f; }
    .tien-step.danger button:active {
      background: #c9302c; }
    .tien-step.danger button.disabled {
      opacity: 0.8; }
      .tien-step.danger button.disabled:active {
        background: #d9534f; }
  .tien-step.danger .step-value {
    border-color: #d9534f; }
    .tien-step.danger .step-value i.fa {
      color: #d9534f;
      font-size: 14px;
      line-height: 20px;
      top: -2px;
      position: relative; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.22/angular.min.js"></script>
<script src="http://angular-step-input.10kb.nl/angular-step-input.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">

<div ng-app="app" ng-controller="AppController">
  <tien-step-input ng-model="demo"></tien-input-step>
</div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.