Fungsi matematika di binding AngularJS

232

Apakah ada cara untuk menggunakan fungsi matematika di binding AngularJS?

misalnya

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

Biola ini menunjukkan masalahnya

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

Ricick
sumber
11
Pilihan lain adalah untuk menghindari menggunakan Matematika dalam template Anda dengan menggunakan filter sebagai gantinya:, <p>The percentage is {{(100*count/total)| number:0}}%</p>lebih banyak komentar di bawah ini.
Andrew Kuklewicz
2
Untuk Angular2, tentukan anggota komponen private Math = Math;dan Anda dapat menggunakannya.
Ondra Žižka

Jawaban:

329

Anda harus menyuntikkan Mathke lingkup Anda, jika Anda perlu menggunakannya karena $scopetidak tahu apa-apa tentang Matematika.

Cara paling sederhana, bisa Anda lakukan

$scope.Math = window.Math;

di controller Anda. Cara sudut untuk melakukan ini dengan benar akan membuat layanan Matematika, saya kira.

Omong kosong
sumber
1
Terima kasih. Saya memiliki kasus penggunaan di mana saya memiliki beberapa template dengan logika yang sama sekali berbeda dan ingin mengisolasinya sebanyak mungkin. Sempurna
Yablargo
48
Anda harus menggunakan filter, bukan meletakkan objek Math pada lingkup.
Soviut
4
Ini bagus untuk hal-hal yang cepat dan kotor; dengan cepat mengejek sesuatu atau ingin melihat bagaimana sesuatu bekerja. Mungkin itulah yang diinginkan seseorang yang ingin melakukan matematika dalam file template html mereka.
Lan
1
Menggunakan fungsi dalam binding akan membuat fungsi untuk memanggil pembaruan setiap lingkup dan akan menggunakan terlalu banyak sumber daya.
desmati
Solusi ini salah . Mengapa? 1- Mencemari ruang lingkup global adalah ide terburuk yang pernah ada. 2- tampilan bertanggung jawab formatting, jadi gunakan filter.
Afshin Mehrabani
304

Meskipun jawaban yang diterima benar bahwa Anda dapat menyuntikkan Mathuntuk menggunakannya dalam sudut, untuk masalah khusus ini, cara yang lebih konvensional / sudut adalah nomor filter:

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

Anda dapat membaca lebih lanjut tentang numberfilter di sini: http://docs.angularjs.org/api/ng/filter/number

Andrew Kuklewicz
sumber
7
Tidak hanya ini cara Angular, ini cara yang benar. Adalah tanggung jawab pandangan untuk 'memformat' nilai.
Lukas
39
Andrew, maaf mengacaukan kiriman Anda dengan komentar ini. Jawaban Anda baik dan bermanfaat; Namun, saya ingin tidak setuju dengan komentar lain. Saya sering merasa ngeri ketika orang-orang memuji "sudut jalan" seolah-olah itu adalah teladan dari perkembangan yang sempurna. Bukan itu. OP secara umum bertanya bagaimana memasukkan fungsi matematika dalam penjilidan, dengan pembulatan disajikan hanya sebagai contoh. Meskipun jawaban ini mungkin "jalan sudut" oleh para puritan untuk menyelesaikan satu masalah matematika, jawaban itu tidak sepenuhnya menjawab pertanyaan.
kbrimington
1
Maaf untuk arkeologi, tetapi ini tidak benar ketika Anda membutuhkan Nomor sebagai output. {{1234.567|number:2}}dirender sebagai 1,234.57atau 1 234,57. Jika Anda mencoba meneruskannya, <input type="number" ng-value="1234.567|number:2">Anda akan mengosongkan input, karena nilainya BUKAN NOMOR YANG BENAR, tetapi representasi string dependen lokal.
SWilk
61

Saya pikir cara terbaik untuk melakukannya adalah dengan membuat filter, seperti ini:

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

maka markupnya terlihat seperti ini:

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

Biola yang diperbarui: http://jsfiddle.net/BB4T4/

sabinstefanescu
sumber
Filter angka seperti yang diberikan dalam jawaban klaxon sudah akan melakukan ceil, jadi tidak perlu untuk itu.
Kim
Saya melakukan sesuatu yang serupa, hanya menggunakan filter untuk membuat timer itu sendiri seperti yang saya inginkan. Dengan kata lain, saya akan mengambil waktu saat ini dan memformatnya menjadi string yang saya lihat cocok menggunakan filter khusus. Juga filter angka baik ketika Anda ingin membulatkan angka Anda, tetapi untuk fungsi lantai dan langit-langit tidak ada gunanya.
Gabriel Lovetro
37

Ini adalah jawaban yang berbulu, karena Anda tidak memberikan konteks penuh dari apa yang Anda lakukan. Jawaban yang diterima akan berhasil, tetapi dalam beberapa kasus akan menyebabkan kinerja yang buruk. Itu, dan akan lebih sulit untuk diuji.

Jika Anda melakukan ini sebagai bagian dari bentuk statis, baiklah. Jawaban yang diterima akan bekerja, meskipun itu tidak mudah untuk diuji, dan itu membingungkan.

Jika Anda ingin menjadi "Sudut" tentang ini:

Anda harus menjaga "logika bisnis" apa pun (yaitu logika yang mengubah data yang akan ditampilkan) dari pandangan Anda. Ini agar Anda dapat menguji logika Anda, dan agar Anda tidak menyambungkan pengontrol dan tampilan Anda dengan erat. Secara teoritis, Anda harus dapat mengarahkan pengontrol Anda di tampilan lain dan menggunakan nilai yang sama dari cakupan. (jika itu masuk akal).

Anda juga harus mempertimbangkan bahwa fungsi apa pun yang memanggil bagian dalam pengikatan (seperti {{}}atau ng-bindatau ng-bind-html) harus dievaluasi pada setiap intisari , karena angular tidak memiliki cara untuk mengetahui apakah nilainya telah berubah atau tidak suka dengan properti. pada ruang lingkup.

Cara "angular" untuk melakukan ini adalah dengan menyimpan nilai di properti pada lingkup perubahan menggunakan peristiwa ng-change atau bahkan $ watch.

Misalnya dengan bentuk statis:

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>

Dan sekarang Anda bisa mengujinya!

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.
  });
});
Ben Lesh
sumber
Anda bisa mengkompilasi elemen dan kemudian menguji nilai yang ditemukan di elemen dom. itu sebenarnya lebih disukai karena Anda mungkin menggunakan filter pelokalan juga.
FlavourScape
Jawaban yang bagus. Tetapi bertanya-tanya mengapa cocok dengan $windowkarena tampaknya bekerja dengan hanya rencana Math.round()?
Neel
3
@Neel - $ window memungkinkan Anda untuk lebih mudah mengejek Mathtes. Secara alternatif, Anda dapat membuat layanan Matematika dan menyuntikkannya.
Ben Lesh
8

Mengapa tidak membungkus seluruh obj matematika dalam 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]);

dan untuk menggunakan:

{{number_var | matematika: 'ceil'}}

Marcos Ammon
sumber
8

Pilihan yang lebih baik adalah menggunakan:

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

Ini menetapkan nilai default persamaan ke 0 dalam kasus ketika nilai tidak diinisialisasi.

klakson
sumber
6

Cara termudah untuk melakukan matematika sederhana dengan Angular adalah langsung di markup HTML untuk binding individual sesuai kebutuhan, dengan asumsi Anda tidak perlu melakukan perhitungan massal di halaman Anda. Ini sebuah contoh:

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

Dalam hal ini Anda cukup menghitung dalam () dan kemudian menggunakan filter | untuk mendapatkan nomor jawaban. Berikut ini info lebih lanjut tentang memformat angka sudut sebagai teks:

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

Sara Inés Calderón
sumber
4

Baik mengikat objek Matematika global ke lingkup (ingat untuk menggunakan $ window bukan window)

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

Gunakan penjilidan di HTML Anda:

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

Atau buat filter untuk fungsi Matematika spesifik yang Anda cari:

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

Gunakan filter dalam HTML Anda:

<p>Distance from zero: {{distance | abs}}</p>
pengrajin
sumber
2

Itu tidak terlihat seperti cara yang sangat Angular untuk melakukannya. Saya tidak sepenuhnya yakin mengapa itu tidak berhasil, tetapi Anda mungkin perlu mengakses ruang lingkup untuk menggunakan fungsi seperti itu.

Saran saya adalah membuat filter. Itu jalan Angular.

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

Kemudian di HTML Anda lakukan ini:

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

The numberFilter membentuk angka dengan pemisah ribuan, sehingga tidak ketat fungsi matematika.

Selain itu, 'limiter' desimalnya tidak ceilberupa desimal cincang (karena beberapa jawaban lain akan membuat Anda percaya), tetapi sebaliknya round.

Jadi untuk fungsi matematika yang Anda inginkan, Anda dapat menyuntikkannya (lebih mudah untuk mengejek daripada menyuntikkan seluruh objek Matematika) seperti:

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

Tidak perlu membungkusnya di fungsi lain juga.


sumber
0

Ini kurang lebih adalah ringkasan dari tiga jawaban (oleh Sara Inés Calderón, klaxon dan Gothburz), tetapi karena mereka semua menambahkan sesuatu yang penting, saya menganggapnya layak untuk bergabung dengan solusi dan menambahkan beberapa penjelasan lebih lanjut.

Mempertimbangkan contoh Anda, Anda dapat melakukan perhitungan dalam template Anda menggunakan:

{{ 100 * (count/total) }}

Namun, ini dapat menghasilkan banyak tempat desimal, jadi menggunakan filter adalah cara yang baik:

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

Secara default, filter angka akan meninggalkan hingga tiga digit fraksional , di sinilah argumen fractionSize sangat berguna ( {{ 100 * (count/total) | number:fractionSize }}), yang dalam kasus Anda adalah:

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

Ini juga akan melengkapi hasilnya:

Hal terakhir yang disebutkan, jika Anda mengandalkan sumber data eksternal, mungkin merupakan praktik yang baik untuk memberikan nilai cadangan yang tepat (jika tidak, Anda mungkin melihat NaN atau tidak sama sekali di situs Anda):

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

Sidenote: Tergantung pada spesifikasi Anda, Anda bahkan mungkin dapat lebih tepat dengan fallback Anda / tentukan fallback pada level yang lebih rendah (mis {{(100 * (count || 10)/ (total || 100) | number:2)}}.). Padahal, ini mungkin tidak selalu masuk akal ..

Kim
sumber
0

Contoh skrip Angular menggunakan pipa.

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

Tambahkan ke deklarasi @NgModule

@NgModule({
  declarations: [
    MathPipe,

kemudian gunakan di template Anda seperti ini:

{{(100*count/total) | math:'round'}}
Joel Davey
sumber
TypeError: Math [args] bukan fungsi
MattEnth