资讯专栏INFORMATION COLUMN

使用指令实现价格单位转换

lewinlee / 982人阅读

摘要:当输入一个价格的时候,指令监听到价格发生变化,然后对输入的价格乘,将元转换成分。

要求:输入的是xxx.xx元,后台保存的是xxxxx分。并添加表单验证。

基本思路

在上面的描述中,包含了两个价格,一个是输入的价格,也就是我们能看到的价格;另一个就是实际传给后台的价格。当输入一个价格的时候,指令监听到价格发生变化,然后对输入的价格乘100,将转换成

指令实现 确定绑定的数据

根据要求,第一个必须绑定的数据就是ng-model中的值,也就是最后传给后台的数据。

另外,需要实现表单验证,所以required属性是必须的,然后有时我们还要给用户一些提示信息,提示这是一个必填项,所以还需要name

scope: {
    ngModel: "=",       // 绑定的价格(以`分`为单位)
    name: "@?",         // 输入框name属性,使用‘@’,获取name属性的值
    required: "=?"      // 表单验证,默认为true;如果不想进行验证,将其设置为false
}  

由于namerequired都不是必须设置的属性,所以使用了

主功能实现

首先我们要初始化一个显示在input框的价格:

scope.price = undefined;

但是考虑到编辑的时候,会从后台获取一个已经存在的价格,这个时候我们还要将其显示出来,所以修改一下:

scope.price = scope.ngModel / 100

接下来就是对我们刚刚定义的价格进行监听了,当其发生变化的时候,对其进行单位转换处理。

// 监听价格
scope.$watch("price", function(newValue) {
    if (newValue) {
        scope.ngModel = newValue * 100; // 将‘元’转换成‘分’
    }
});

由于在监听的时候,当删除所有输入后,price的值就是undefined了,也就是newValueundefined,所以这时ngModel中还是上一次的值。所以要做一下清空处理。

scope.$watch("price", function(newValue) {
    if (newValue) {
        scope.ngModel = newValue * 100; // 将‘元’转换成‘分’
    }
    
    // 防止删除所有的输入后,ngModel还有值
    if (typeof(scope.price) === "undefined") {
        scope.ngModel = 0;
    }
});

这个时候我们的效果就是这样的:

再测试一下编辑时的效果:

出问题了,实际传给后台的价格有,但是并没有显示成元,也就是说我们设置的初始值没有生效。

初始化之前打印一下ngModel

造成这个的原因是因为angular在进行渲染的时候很快,当我们定义price的时候,ngModel的值还么有绑定过来,所以这里就是undefined了。

解决办法就是让他延迟一会再进行初始化。

$timeout(function() {
    scope.price = scope.ngModel / 100; // 实际显示的价格(以’元‘为单位)
}, 100);

再来看下编辑的效果:

验证的实现

最开始我们在绑定的时候选择了两个属性:namerequired然后我们将他绑定在输入框上:

在指令中用一下:

然后看看验证的效果:


功能补充

从上面的效果我们可以看到,还没有真正的满足我们对价格输入的期待:

1.控制两位小数
2.不能出现输入多个小数点的情况

所以,为了使用户的体验更加好,这里还需要做一点改进,对价格做一下格式化。

显示价格的格式化

首先我们要控制价格显示两位小数,大概要按照下面的思路来处理:

1.获取小数点的位置
2.获取小数点后面的部分

如果没有后面的部分,添加00

如果后面的部分只有一位,添加一个0

如果后面的部分有两位及以上,截取到两位的部分

所以按照上面的思路,我们就能实现普遍的金钱显示的效果。然后再考虑小数点的问题。

因为为了使用户输入的时候能够显示xx.00的样子,所以我们在前台显示的时候,就必须将price的类型换成是字符串,而一旦我们的类型是字符串,就意味着用户可以输入不止一个小数点,我们要做的就是禁止用户输入多个小数点。

接着上面的思路继续:

3.从第一个小数点后面的部分,获取第二个小数点的位置。

如果位置下标为负数,说明第一个小数点后面的部分是一个整数,不存在第二个小数点

如果下标不为负数,说明第一个小数点后面的部分存在第二个小数点,我们只保留到第二个小数点前面的那部分

按照这个思路,我们可以整理出下面的代码:

self.format = function(price) {
    // 获取小数点后的数字,并计算长度
    var firstPoint = price.indexOf("."); // 获取小数点的位置

    if (firstPoint >= 0) {
        var mantissa = price.slice(firstPoint + 1);  // 获取小数点后面的部分
        var secondPoint = mantissa.indexOf("."); // 获取第二个小数的位置,防出现输入两个及以上的小数点的情况

        if (secondPoint < 0) {
            // 如果小数点后超过两位,去掉后面的
            if (mantissa.length >= 2) {
                return price.slice(0, firstPoint + 3);
            } else if (mantissa.length === 1) {
                return price + "0";
            }
            } else {
                // 去除第二个小数点
                return price.slice(0, firstPoint + secondPoint + 1);
            }
         } else {
             return price + ".00";
         }
};

这里小数点的格式化完成了,但是又出现了另一个问题,就是我们什么时候调用的问题。最开始,我是在监听ngModel变化的时候去调用这个函数。大部分的功能都是没问题的,就是控制小数点不行。

上面已经说了,输入的价格price是一个字符串,这时候如果我们连着输入两个小数点。比如12..,当我们输入12的时候,ngModel的值是12,;当我们输入12.的时候,ngModel还是12;当输入两个点12..的时候,还是12ngModel的值没变,所以不会触发格式化的函数,也就控制不了小数点的个数。同样的,当我们小数点后面连续输入多个0也是这个道理。

所以就在监听价格的时候,去触发格式化的函数。

// 监听价格
self.watchPrice = function(newValue) {
    if (newValue) {
        var point = newValue.indexOf("."); // 获取小数点的位置

        // 防止在输入的时候,当出现输入一个整数的时候,会自动补全小数点
        // 所以这里只有在小数点后的位数超过两位的时候
        // 或者出现两个小数点的时候才触发格式化函数
        if (point >= 0) {
            var mantissa = newValue.slice(point + 1);
            var secondPoint = mantissa.indexOf(".");

            if (mantissa.length >= 2 || secondPoint >= 0) {
                scope.price = self.format(newValue);
            }
        } else {
            scope.price = newValue;
        }

        scope.ngModel = parseFloat(scope.price) * 100;  // 将‘元’转换成‘分’
    }
    ...
}

效果图:

完整代码:

angular.module("webappApp")
    .directive("yunzhiPrice", function($timeout) {
        return {
            // 独立scope
            scope: {
                ngModel: "=", // 绑定的价格(以`分`为单位)
                name: "@?", // 输入框name属性,使用‘@’,获取name属性的值
                required: "=?" // 表单验证,默认为true;如果不想进行验证,将其设置为false
            },
            templateUrl: "/views/directive/yunzhiPrice.html",
            restrict: "E",
            link: function postLink(scope) {
                var self = this;

                // 初始化
                self.init = function() {
                    // 这里由于最开始渲染的时候,ngModel还没有值,所以延迟一会再进行赋初值
                    $timeout(function() {
                        scope.price = self.format((scope.ngModel / 100).toString()); // 实际显示的价格(以’元‘为单位)
                    }, 100);
                    scope.$watch("price", self.watchPrice);
                    scope.$watch("ngModel", self.watchModel);
                };

                // 默认进行验证
                if (typeof(scope.required) === "undefined") {
                    scope.required = true;
                }

                // 监听价格
                self.watchPrice = function(newValue) {
                    if (newValue) {
                        var point = newValue.indexOf("."); // 获取小数点的位置

                        // 防止在输入的时候,当出现输入一个整数的时候,会自动补全小数点
                        // 所以这里只有在小数点后的位数超过两位的时候
                        // 或者出现两个小数点的时候才触发格式化函数
                        if (point >= 0) {
                            var mantissa = newValue.slice(point + 1);
                            var secondPoint = mantissa.indexOf(".");

                            if (mantissa.length >= 2 || secondPoint >= 0) {
                                scope.price = self.format(newValue);
                            }
                        } else {
                            scope.price = newValue;
                        }

                        scope.ngModel = parseFloat(scope.price) * 100;  // 将‘元’转换成‘分’
                    }

                    // 防止删除所有的输入后,ngModel还有值
                    if (typeof(scope.price) === "undefined") {
                        scope.ngModel = 0;
                    }
                };

                // 监听ngModel
                self.watchModel = function(newValue) {
                    if (newValue) {
                        scope.price = (newValue / 100).toString();
                    }
                };

                // 价格格式化函数
                // 整数:在后面添加’.00‘
                // 一位小数:添加一个0
                // 两位小数: 不做改变
                // 两位以上:只截取两位的部分
                self.format = function(price) {
                    // 获取小数点后的数字,并计算长度
                    var firstPoint = price.indexOf("."); // 获取小数点的位置

                    if (firstPoint >= 0) {
                        var mantissa = price.slice(firstPoint + 1);
                        var secondPoint = mantissa.indexOf("."); // 获取第二个小数的位置,防出现输入两个及以上的小数点的情况

                        if (secondPoint < 0) {
                            // 如果小数点后超过两位,去掉后面的
                            if (mantissa.length >= 2) {
                                return price.slice(0, firstPoint + 3);
                            } else if (mantissa.length === 1) {
                                return price + "0";
                            }
                        } else {
                            // 去除第二个小数点
                            return price.slice(0, firstPoint + secondPoint + 1);
                        }
                    } else {
                        return price + ".00";
                    }
                };

                self.init();
            }
        };
    });
总结

每一个复杂的问题,只要将其拆分成简单的小问题,都会变得简单。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/58159.html

相关文章

  • 王道操作系统(1-15)学习总结

    摘要:多道批处理系统操作系统引入中断,磁盘能够输出多道程序,并且程序可以通过操作系统来管理并发执行。输入完多道程序之后能够通过操作系统来进行调度,最后输出结果。分时操作系统以时间片为单位的轮流为每个用户作业服务。 ...

    amc 评论0 收藏0
  • Vim 的哲学(四)

    摘要:的哲学第四篇姗姗来迟,狗血的原因我就不多说了,好消息是我将为这个系列带来一些动态演示。所以目前为止你学会上述四个动作指令就足够了。以词为单位使得我们可以更精确也是更具语义化的移动光标,并且要比逐个字符的移动要快得多。 Vim 的哲学第四篇姗姗来迟,狗血的原因我就不多说了,好消息是我将为这个系列带来一些动态演示。原本我打算录视频的,但是文章都写了那么些篇了,现在再录视频似乎晚了些,所以我...

    xiao7cn 评论0 收藏0
  • 局部性原理——各类优化的基石

    摘要:基于局部性原理,计算机处理器在设计时做了各种优化,比如现代的多级分支预测有良好局部性的程序比局部性差的程序运行得更快。目前计算机设计中,都是以块页为单位管理调度存储,其实就是在利用空间局部性来优化性能。   学过计算机底层原理、了解过很多架构设计或者是做过优化的同学,应该很熟悉局部性原理。即便是非计算机行业的人,在做各种调优、提效时也不得不考虑到局部性,只不过他们不常用局部性一词。如果...

    MadPecker 评论0 收藏0
  • 字节码及ASM使用

    摘要:字节码及使用什么是字节码机器码机器码是可直接解读的指令。字节码的执行操作,指的就是对当前栈帧数据结构进行的操作。动态链接每个栈帧指向运行时常量池中该栈帧所属的方法的引用,也就是字节码的发放调用的引用。 字节码及ASM使用 什么是字节码? 机器码机器码(machine code)是CPU可直接解读的指令。机器码与硬件等有关,不同的CPU架构支持的硬件码也不相同。 字节码字节码(byte...

    hearaway 评论0 收藏0
  • Java并发编程之背景知识

    摘要:其实现在打开一个程序的意思是打开一个进程并且打开若干个于这个进程相关联的线程。 操作系统发展回顾 裸机 老早之前的计算机只有一个处理器,而一个处理器在同一时刻只能处理一条指令,换句话说,我们的代码需要一行一行的按顺序被计算机执行,计算机只能把一个程序完整的执行完,然后再执行第二个程序。所以计算机专业的同学们要排队去机房做实验,一个人执行完然他的程序后,第二个人再执行自己的程序,这也就...

    dendoink 评论0 收藏0

发表评论

0条评论

lewinlee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<