首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用AngularJS指令格式化输入字段,同时保持范围变量不变

使用AngularJS指令格式化输入字段,同时保持范围变量不变
EN

Stack Overflow用户
提问于 2013-09-30 20:20:29
回答 4查看 51K关注 0票数 22

我在格式化输入字段时遇到了问题,而底层的作用域变量却没有格式化。

我想要实现的是一个显示货币的文本域。它应该在处理错误输入的同时动态地格式化自己。我让它工作了,但我的问题是我想要将非格式化的值存储在我的作用域变量中。输入的问题是它需要一个双向的模型,所以更改输入字段会更新模型,反之亦然。

我偶然发现了$parsers$formatters,这似乎就是我要找的。不幸的是,它们并不相互影响(这实际上可能有助于避免无穷无尽的循环)。

我创建了一个简单的jsFiddle:http://jsfiddle.net/cruckie/yE8Yj/,代码如下:

HTML:

代码语言:javascript
复制
<div data-ng-app="app" data-ng-controller="Ctrl">
    <input type="text" data-currency="" data-ng-model="data" />
    <div>Model: {{data}}</div>
</div>

JS:

代码语言:javascript
复制
var app = angular.module("app", []);

function Ctrl($scope) {
    $scope.data = 1234567;
}

app.directive('currency', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attr, ctrl) {

            ctrl.$formatters.push(function(modelValue) {
                return modelValue.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
            });

            ctrl.$parsers.push(function(viewValue) {
                return parseFloat(viewValue.replace(new RegExp(",", "g"), ''));
            });
        }
    };
});

同样,这只是一个简单的例子。当它加载时,一切看起来都像它应该做的那样。输入字段已格式化,而变量未格式化。但是,当更改输入字段中的值时,它不再对自身进行格式化-但变量会正确更新。

有没有一种方法可以确保文本字段被格式化,而变量没有格式化?我猜我正在寻找的是一个文本字段的过滤器,但我看不到任何关于它的东西。

诚挚的问候

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-10-01 00:52:21

这里有一个小提琴,展示了我如何在我的应用程序中实现完全相同的行为。我最终使用了ngModelController#render而不是$formatters,然后添加了一组在keydownchange事件上触发的单独的行为。

http://jsfiddle.net/KPeBD/2/

票数 17
EN

Stack Overflow用户

发布于 2015-04-22 16:40:40

我重构了原始指令,以便它使用$parses和$formatters,而不是侦听键盘事件。也不需要使用$browser.defer

请在此处查看工作演示http://jsfiddle.net/davidvotrubec/ebuqo6Lm/

代码语言:javascript
复制
    var myApp = angular.module('myApp', []);

    myApp.controller('MyCtrl', function($scope) {
      $scope.numericValue = 12345678;
    });

    //Written by David Votrubec from ST-Software.com
    //Inspired by http://jsfiddle.net/KPeBD/2/
    myApp.directive('sgNumberInput', ['$filter', '$locale', function ($filter, $locale) {
            return {
                require: 'ngModel',
                restrict: "A",
                link: function ($scope, element, attrs, ctrl) {
                    var fractionSize = parseInt(attrs['fractionSize']) || 0;
                    var numberFilter = $filter('number');
                    //format the view value
                    ctrl.$formatters.push(function (modelValue) {
                        var retVal = numberFilter(modelValue, fractionSize);
                        var isValid = isNaN(modelValue) == false;
                        ctrl.$setValidity(attrs.name, isValid);
                        return retVal;
                    });
                    //parse user's input
                    ctrl.$parsers.push(function (viewValue) {
                        var caretPosition = getCaretPosition(element[0]), nonNumericCount = countNonNumericChars(viewValue);
                        viewValue = viewValue || '';
                        //Replace all possible group separators
                        var trimmedValue = viewValue.trim().replace(/,/g, '').replace(/`/g, '').replace(/'/g, '').replace(/\u00a0/g, '').replace(/ /g, '');
                        //If numericValue contains more decimal places than is allowed by fractionSize, then numberFilter would round the value up
                        //Thus 123.109 would become 123.11
                        //We do not want that, therefore I strip the extra decimal numbers
                        var separator = $locale.NUMBER_FORMATS.DECIMAL_SEP;
                        var arr = trimmedValue.split(separator);
                        var decimalPlaces = arr[1];
                        if (decimalPlaces != null && decimalPlaces.length > fractionSize) {
                            //Trim extra decimal places
                            decimalPlaces = decimalPlaces.substring(0, fractionSize);
                            trimmedValue = arr[0] + separator + decimalPlaces;
                        }
                        var numericValue = parseFloat(trimmedValue);
                        var isEmpty = numericValue == null || viewValue.trim() === "";
                        var isRequired = attrs.required || false;
                        var isValid = true;
                        if (isEmpty && isRequired) {
                            isValid = false;
                        }
                        if (isEmpty == false && isNaN(numericValue)) {
                            isValid = false;
                        }
                        ctrl.$setValidity(attrs.name, isValid);
                        if (isNaN(numericValue) == false && isValid) {
                            var newViewValue = numberFilter(numericValue, fractionSize);
                            element.val(newViewValue);
                            var newNonNumbericCount = countNonNumericChars(newViewValue);
                            var diff = newNonNumbericCount - nonNumericCount;
                            var newCaretPosition = caretPosition + diff;
                            if (nonNumericCount == 0 && newCaretPosition > 0) {
                                newCaretPosition--;
                            }
                            setCaretPosition(element[0], newCaretPosition);
                        }
                        return isNaN(numericValue) == false ? numericValue : null;
                    });
                } //end of link function
            };
            //#region helper methods
            function getCaretPosition(inputField) {
                // Initialize
                var position = 0;
                // IE Support
                if (document.selection) {
                    inputField.focus();
                    // To get cursor position, get empty selection range
                    var emptySelection = document.selection.createRange();
                    // Move selection start to 0 position
                    emptySelection.moveStart('character', -inputField.value.length);
                    // The caret position is selection length
                    position = emptySelection.text.length;
                }
                else if (inputField.selectionStart || inputField.selectionStart == 0) {
                    position = inputField.selectionStart;
                }
                return position;
            }
            function setCaretPosition(inputElement, position) {
                if (inputElement.createTextRange) {
                    var range = inputElement.createTextRange();
                    range.move('character', position);
                    range.select();
                }
                else {
                    if (inputElement.selectionStart) {
                        inputElement.focus();
                        inputElement.setSelectionRange(position, position);
                    }
                    else {
                        inputElement.focus();
                    }
                }
            }
            function countNonNumericChars(value) {
                return (value.match(/[^a-z0-9]/gi) || []).length;
            }
            //#endregion helper methods
        }]);

Github代码在这里[https://github.com/ST-Software/STAngular/blob/master/src/directives/SgNumberInput]

票数 5
EN

Stack Overflow用户

发布于 2013-09-30 20:43:28

实际上,正如您所说的那样,$parsers$formatters是“独立的”(很可能是循环)。在我们的应用程序中,我们使用onchange事件(在link函数中)显式格式化,大致如下:

代码语言:javascript
复制
element.bind("change", function() {
    ...
    var formattedModel = format(ctrl.$modelValue);
    ...
    element.val(formattedModel);
});

有关详细的工作示例,请参阅您的更新小提琴:http://jsfiddle.net/yE8Yj/1/

我喜欢绑定到onchange事件,因为我发现在用户键入时更改输入很烦人。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19094150

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档