AngularJS绑定中的数学函数

有没有办法在AngularJS绑定中使用数学函数?

如。

<p>The percentage is {{Math.round(100*count/total)}}%</p>

这把小提琴说明了这个问题

http://jsfiddle.net/ricick/jtA99/1/

197234 次浏览
你必须注入Math到你的作用域,如果你需要使用它作为 $scope对数学一无所知。

你可以用最简单的方法

$scope.Math = window.Math;

在你的控制器。 Angular的正确方法是创建一个Math服务

虽然公认的答案是正确的,你可以注入Math来在angular中使用它,对于这个特定的问题,更传统的/angular方式是数字过滤器:

<p>The percentage is \{\{(100*count/total)| number:0}}%</p>

你可以在这里阅读有关number过滤器的更多信息:http://docs.angularjs.org/api/ng/filter/number

这是一个棘手的问题,因为你没有给出你正在做的事情的完整背景。接受的答案是可行的,但在某些情况下会导致较差的性能。这一点,而且它将更难测试。

如果你把它作为静态表单的一部分,没问题。公认的答案是有效的,即使它不容易测试,而且它很古怪。

如果你想成为“Angular”:

你需要将任何“业务逻辑”(即改变要显示的数据的逻辑)排除在视图之外。这样你就可以对你的逻辑进行单元测试,这样你就不会把控制器和视图紧密耦合起来。理论上,您应该能够将控制器指向另一个视图,并使用作用域中的相同值。(如果有道理的话)。

你还需要考虑绑定内部的任何函数调用(比如\{\{}}ng-bindng-bind-html)都必须在每一个消化上求值,因为angular无法知道值是否发生了变化,或者是否像作用域上的属性那样。

“angular”的方法是在变化时使用ng-change事件或甚至$watch将值缓存到作用域的属性中。

例如,对于静态表单:

angular.controller('MainCtrl', function($scope, $window) {
$scope.count = 0;
$scope.total = 1;


$scope.updatePercentage = function () {
$scope.percentage = $window.Math.round((100 * $scope.count) / $scope.total);
};
});
<form name="calcForm">
<label>Count <input name="count" ng-model="count"
ng-change="updatePercentage()"
type="number" min="0" required/></label><br/>
<label>Total <input name="total" ng-model="total"
ng-change="updatePercentage()"
type="number" min="1" required/></label><br/>
<hr/>
Percentage: \{\{percentage}}
</form>

现在您可以测试它了!

describe('Testing percentage controller', function() {
var $scope = null;
var ctrl = null;


//you need to indicate your module in a test
beforeEach(module('plunker'));


beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();


ctrl = $controller('MainCtrl', {
$scope: $scope
});
}));


it('should calculate percentages properly', function() {
$scope.count = 1;
$scope.total = 1;
$scope.updatePercentage();
expect($scope.percentage).toEqual(100);


$scope.count = 1;
$scope.total = 2;
$scope.updatePercentage();
expect($scope.percentage).toEqual(50);


$scope.count = 497;
$scope.total = 10000;
$scope.updatePercentage();
expect($scope.percentage).toEqual(5); //4.97% rounded up.


$scope.count = 231;
$scope.total = 10000;
$scope.updatePercentage();
expect($scope.percentage).toEqual(2); //2.31% rounded down.
});
});

我认为最好的方法是创建一个过滤器,就像这样:

myModule.filter('ceil', function() {
return function(input) {
return Math.ceil(input);
};
});

然后标记看起来像这样:

<p>The percentage is \{\{ (100*count/total) | ceil }}%</p>

更新小提琴:http://jsfiddle.net/BB4T4/

将全局Math对象绑定到作用域(记住使用$window而不是window)

$scope.abs = $window.Math.abs;

在你的HTML中使用绑定:

<p>Distance from zero: \{\{abs(distance)}}</p>

或者为特定的Math函数创建一个过滤器:

module.filter('abs', ['$window', function($window) {
return function(n) {
return $window.Math.abs($window.parseInt(n));
};
});

在你的HTML中使用过滤器:

<p>Distance from zero: \{\{distance | abs}}</p>

如果你想在Angular中做一个简单的循环,你可以很容易地在表达式中设置过滤器。例如:

\{\{ val | number:0 }}

请看这个CodePen的例子&对于其他数字过滤器选项。

Angular Docs on using Number Filters

更好的选择是使用:

\{\{(100*score/questionCounter) || 0 | number:0}}

在值未初始化的情况下,它将方程的默认值设置为0。

使用Angular进行简单计算的最简单方法是根据需要直接在HTML标记中进行单个绑定,假设你不需要在页面上进行大量计算。这里有一个例子:

\{\{(data.input/data.input2)| number}}

在本例中,您只需在()中进行数学运算,然后使用筛选器|来获得数字答案。这里有更多关于将Angular数字格式化为文本的信息:

https://docs.angularjs.org/api/ng/filter

为什么不把整个数学对象包装在一个过滤器中呢?

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




function math() {
return function(input,arg) {
if(input) {
return Math[arg](input);
}


return 0;
}
}


return app.filter('math',[math]);

并使用:

\{\{number_var | math:'ceil'}}

这或多或少是三个答案的总结(来自Sara Inés Calderón, klaxon和Gothburz),但由于他们都添加了一些重要的东西,我认为值得加入解决方案并添加更多的解释。

考虑到你的例子,你可以在你的模板中使用:

\{\{ 100 * (count/total) }}

然而,这可能会导致一大堆小数点后的位置,所以使用过滤器是一个很好的方法:

\{\{ 100 * (count/total) | number }}
默认情况下,数字过滤器将最多留下三个小数位数,这就是fractionSize论点非常方便的地方 (\{\{ 100 * (count/total) | number:fractionSize }}),在你的情况下将是:

\{\{ 100 * (count/total) | number:0 }}
这也将四舍五入的结果已经:
angular.module('numberFilterExample', [])
.controller('ExampleController', ['$scope',
function($scope) {
$scope.val = 1234.56789;
}
]);
<!doctype html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="numberFilterExample">
<table ng-controller="ExampleController">
<tr>
<td>No formatting:</td>
<td>
<span>\{\{ val }}</span>
</td>
</tr>
<tr>
<td>3 Decimal places:</td>
<td>
<span>\{\{ val | number }}</span> (default)
</td>
</tr>
<tr>
<td>2 Decimal places:</td>
<td><span>\{\{ val | number:2 }}</span></td>
</tr>
<tr>
<td>No fractions: </td>
<td><span>\{\{ val | number:0 }}</span> (rounded)</td>
</tr>
</table>
</body>
</html>

最后要提到的是,如果你依赖于外部数据源,提供一个合适的回退值可能是一个很好的练习(否则你可能在你的网站上看到NaN或什么都没有):

\{\{ (100 * (count/total) | number:0) || 0 }}

旁注:根据您的规范,您甚至可以更精确地使用回退/已经在较低级别上定义回退(例如\{\{(100 * (count || 10)/ (total || 100) | number:2)}})。然而,这可能并不总是有意义的。

这看起来不像是一种非常Angular的方式。我不完全确定为什么它不起作用,但您可能需要访问作用域才能使用这样的函数。

我的建议是创建一个过滤器。这就是Angular的方式。

myModule.filter('ceil', function() {
return function(input) {
return Math.ceil(input);
};
});

然后在你的HTML中这样做:

<p>The percentage is \{\{ (100*count/total) | ceil }}%</p>

number过滤器使用数千个分隔符格式化数字,因此它不是严格意义上的数学函数。

此外,它的十进制“限制器”并没有ceil任何切碎的小数(正如其他一些答案会让你相信的那样),而是rounding它们。

因此,对于任何你想要的数学函数,你可以像这样注入它(模仿比注入整个math对象更容易):

myModule.filter('ceil', function () {
return Math.ceil;
});

也不需要将它包装在另一个函数中。

使用管道的Angular Typescript示例。

math.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';


@Pipe({
name: 'math',
})


export class MathPipe implements PipeTransform {


transform(value: number, args: any = null):any {
if(value) {
return Math[args](value);
}
return 0;
}
}

添加到@NgModule声明中

@NgModule({
declarations: [
MathPipe,

然后在模板中像这样使用:

\{\{(100*count/total) | math:'round'}}