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žka2017

回答:


329

Mathについて何も知らないMathように使用する必要がある場合は、スコープに挿入する必要があります $scope

最も簡単な方法は、あなたができる

$scope.Math = window.Math;

コントローラで。これを正しく行うための角度的な方法は、Mathサービスを作成することだと思います。


1
ありがとう。完全に異なるロジックを持つ複数のテンプレートがあり、それらをできるだけ分離したいというユースケースがあります。完璧
Yablargo 2013年

48
スコープにMathオブジェクトを配置するのではなく、フィルターを使用する必要があります。
ソビエト共和国

4
これは、すばやく汚れたものに適しています。何かをすぐにモックアウトしたり、何かがどのように機能するかを確認したい。これはおそらく、htmlテンプレートファイルで数学を実行したい人が望んでいることです。
Lan

1
バインディングで関数を使用すると、各スコープの更新で関数が呼び出され、リソースが大量に使用されます。
desmati 2016

この解決策は間違っています。どうして?1-グローバルスコープの汚染は、これまでで最悪の考えです。2-ビューがを担当するためformatting、代わりにフィルターを使用してください。
Afshin Mehrabani 2016年

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は一般に、算術関数をバインディングに含める方法を尋ねました。丸めは例としてのみ示しています。この答えは純粋主義者が正確に1つの数学の問題を解決するための「角度のある方法」かもしれませんが、質問に完全に答えるものではありません。
kbrimington 2014

1
考古学では申し訳ありませんが、出力として数値が必要な場合は正しくありません。または{{1234.567|number:2}}としてレンダリングします。これを渡そうとすると、入力は空になります。値は正しい数字ではなく、ロケールに依存する文字列表現だからです。1,234.571 234,57<input type="number" ng-value="1234.567|number:2">
SWilk 2017年

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/


klaxonの回答で与えられた数値フィルターはすでにceilを実行するので、その必要はありません。
キム

私は同様のことを行いましたが、フィルターを使用して、タイマー自体を望みどおりに作成しました。言い換えれば、カスタムフィルターを使用して適切であると判断した現在の時刻を文字列にフォーマットします。また、数値フィルターは数値を丸めたい場合に適していますが、床と天井の機能には役に立ちません。
Gabriel Lovetro 2018年

37

あなたがしていることの完全なコンテキストを提供しなかったので、これは答える毛深いものです。受け入れられた答えは機能しますが、場合によってはパフォーマンスが低下します。それは、テストするのが難しくなるでしょう。

静的フォームの一部としてこれを行っている場合は、問題ありません。簡単にテストできなくても、受け入れられた答えは機能します。

これについて「角度」をつけたい場合:

ビューから「ビジネスロジック」(つまり、表示されるデータを変更するロジック)を除外する必要があります。これは、ロジックを単体テストできるようにするためであり、コントローラーとビューを緊密に結合することにはなりません。理論的には、コントローラーを別のビューに向け、スコープの同じ値を使用できるはずです。(それが理にかなっている場合)。

バインディング内の関数呼び出し({{}}またはor ng-bindなどng-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を使用するとMath、テスト用のモックをより簡単に作成できます。あるいは、Mathサービスを作成して、それを注入することもできます。
Ben Lesh 2015

8

数学オブジェクト全体をフィルターで包みませんか?

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


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を使用することを忘れないでください)。

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

HTMLでバインディングを使用します。

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

または、特定の数学関数用のフィルターを作成します。

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それは厳密に数学の機能はありませんので、フィルターは、桁区切りで番号をフォーマットします。

さらに、その10進数の「リミッター」はceil切り刻まれた10進数ではなく(他のいくつかの答えはあなたを信じさせるでしょう)、むしろroundそれらを使用します。

したがって、任意の数学関数について、次のように関数を注入できます(Mathオブジェクト全体を注入するよりもモックする方が簡単です)。

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

別の関数でラップする必要もありません。


0

これは多かれ少なかれ3つの回答の要約です(SaraInésCalderón、klaxon、およびGothburzによる)。これらはすべて重要なことを追加したので、解決策に加わって説明を追加する価値があると思います。

あなたの例を考えると、あなたはテンプレートを使って計算を行うことができます:

{{ 100 * (count/total) }}

ただし、これにより小数点以下の桁数が多くなる可能性があるため、フィルターを使用することをお勧めします。

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

デフォルトでは、数値フィルターは最大3桁の小数桁残します。これは、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の例。

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

TypeError:Math [args]は関数ではありません
MattEnth
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.