AngularJS绑定中的数学函数


232

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

例如

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

这个小提琴显示了问题

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


11
另一个选择是通过使用过滤器来避免在模板中使用Math:,<p>The percentage is {{(100*count/total)| number:0}}%</p>下面有更多评论。
Andrew Kuklewicz 2014年

2
对于Angular2,定义一个组件成员private Math = Math;即可使用。
Ondra参观Žižka

Answers:


329

Math如果您$scope对数学一无所知,则必须注入示波器 。

最简单的方法,你可以做

$scope.Math = window.Math;

在您的控制器中。我猜想正确地做到这一点的角度方法是创建一个数学服务。


1
谢谢。我有一个用例,其中有多个模板具有完全不同的逻辑,并希望尽可能地隔离它们。完美
Yablargo 2013年

48
您应该使用过滤器,而不要将Math对象放在示波器上。
苏联2014年

4
这对快速而肮脏的东西很有好处。快速嘲笑某件事或想看看某事是如何工作的。可能是想要在其html模板文件中进行数学运算的人想要的。
局域网

1
在绑定中使用函数会使函数在每个作用域的更新中调用,并且会占用过多资源。
desmati

此解决方案是错误的。为什么?1-污染全球范围是最糟糕的想法。2-视图负责formatting,因此请使用过滤器。
Afshin Mehrabani

304

虽然可以接受Math以角度来使用它的答案是正确的,但是对于这个特定问题,更常规/角度的方法是数字过滤器:

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

您可以在number此处了解有关过滤器的更多信息:http : //docs.angularjs.org/api/ng/filter/number


7
这不仅是Angular方式,也是正确的方式。“格式化”值是视图的责任。
路加福音

39
安德鲁,很抱歉,此评论使您的帖子混乱。您的回答很好,很有帮助;但是,我希望不同意其他意见。当人们吹捧“成角度的方式”时,我常常感到不安,好像这是完美发展的典范。不是。OP通常询问如何在绑定中包括数学函数,而四舍五入仅作为示例。虽然这个答案可能是纯粹主义者解决确切的一个数学问题的“有角度的方法”,但它并不能完全回答这个问题。
KBrimington

1
抱歉,考古学,但是当您需要数字作为输出时,这是不正确的。{{1234.567|number:2}}呈现为1,234.571 234,57。如果尝试将其传递给<input type="number" ng-value="1234.567|number:2">您,则将清空输入内容,因为该值不是正确的数字,而是与语言环境相关的字符串表示形式。
SWilk

61

我认为最好的方法是创建一个过滤器,如下所示:

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

那么标记看起来像这样:

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

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


卡拉克森答案中给出的数字过滤器已经完成了ceil,因此不需要这样做。

我做了类似的事情,只是使用过滤器根据需要创建计时器本身。换句话说,我会使用自定义过滤器将当前时间格式化为一个我认为合适的字符串。同样,当您想四舍五入数字时,数字过滤器也很好,但是对于地板和天花板功能则没有用。
加布里埃尔·洛夫特罗

37

这是一个毛茸茸的回答,因为您没有提供正在执行操作的全部上下文。可接受的答案会起作用,但是在某些情况下会导致性能不佳。那样,它将变得更加难以测试。

如果您要以静态形式进行此操作,则可以。即使不容易测试,而且很棘手,接受的答案仍然有效。

如果您想对此一视同仁:

您将希望将任何“业务逻辑”(即改变要显示的数据的逻辑)置于视图之外。这样一来,您就可以对逻辑进行单元测试,从而不会最终使控制器和视图紧密耦合。从理论上讲,您应该能够将控制器指向另一个视图,并使用范围中的相同值。(如果有道理)。

您还需要考虑的是,绑定中的任何函数调用(例如{{}}or ng-bindng-bind-html)都必须在每个摘要中进行求值,因为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.
  });
});

您可以编译该元素,然后测试dom元素中的值。实际上这是首选,因为您也可能会使用本地化过滤器。
FlavorScape 2014年

好答案。但是,想知道为什么要优先考虑$window它,因为它似乎只适用于计划Math.round()吗?
Neel

3
@Neel-$ window使您可以更轻松地模拟Mathfor测试。或者,您可以创建一个Math服务并将其注入。
Ben Lesh

8

为什么不将整个数学obj包装在过滤器中?

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 | 数学:'ceil'}}


8

更好的选择是使用:

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

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



6

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

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

在这种情况下,您只需在()中进行数学运算,然后使用过滤器| 得到一个数字答案。以下是有关将角度数字设置为文本格式的更多信息:

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


4

将全局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>

2

这看起来并不像Angular那样。我不完全确定为什么它不起作用,但是您可能需要访问作用域才能使用类似的功能。

我的建议是创建一个过滤器。那就是角度的方式。

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

然后在您的HTML中执行以下操作:

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

1

number过滤器格式,千位分隔符的数量,所以它不是严格意义上的数学函数。

而且,它的十进制“限制符”不是ceil任何切碎的十进制(因为某些其他答案会使您相信),而是round对它们进行了编码。

因此,对于所需的任何数学函数,都可以像这样注入它(比注入整个Math对象更容易模拟):

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

也无需将其包装在另一个函数中。


0

这或多或少是三个答案的总结(SaraInésCalderón,klaxon和Gothburz),但由于它们都添加了重要内容,因此我认为值得加入解决方案并添加更多解释。

考虑到您的示例,您可以使用以下方法在模板中进行计算:

{{ 100 * (count/total) }}

但是,这可能会导致很多小数位,因此使用过滤器是一种不错的方法:

{{ 100 * (count/total) | number }}

默认情况下,数字过滤器将最多保留三位小数位数,在该位置,fractionSize参数非常方便({{ 100 * (count/total) | number:fractionSize }}),在您的情况下为:

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

这还将舍入结果:

最后要提到的是,如果您依赖外部数据源,则提供适当的后备值(可能会在网站上看到NaN或什么都看不到)可能是一个好习惯:

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

旁注:根据您的规范,您甚至可以通过在较低级别(例如{{(100 * (count || 10)/ (total || 100) | number:2)}})上的后备/定义后备来更加精确。不过,这可能并不总是有意义。


0

使用管道的Angular Typescript示例。

数学管道

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'}}

TypeError:Math [args]不是函数
MattEnth
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.