AngularJS - 输入数字微调器,手动输入数字时无法正常工作

AngularJS - input number spinner, not working correctly when number is inputted manually

我在 Whosebug 上找到了一个用户为 angularJS 制作的指令,用于输入数字微调器,我正在寻找它!

一切正常,但有一件事不是,当你在输入字段中手动输入一个数字,然后按减号,它会从该数字中减去 1,这里一切正常,

但是当您输入一个数字并首先按下加号时,它不会加 1,而是会在您输入的数字旁边添加一个 "1"! 修复后只去 "minus",

有人可以帮我修复代码以避免这种情况吗?我希望当我手动输入号码时,您可以按 "plus" 并在该号码上获得 +1

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

那是因为 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(因为 "10" + 1 结果会给你“101”)。由于您也在使用 numericOnly 指令,因此它似乎是进行转换的正确位置,因为您只会在一个地方挥手进行转换。这是一个例子:

(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>

更新: 允许将 min/max 传递到指令中的更简单的代码:

(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>

(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>