Các hàm toán học trong các ràng buộc AngularJS


232

Có cách nào để sử dụng các hàm toán học trong các ràng buộc AngularJS không?

ví dụ

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

Fiddle này cho thấy vấn đề

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


11
Một tùy chọn khác là tránh sử dụng Math trong các mẫu của bạn bằng cách sử dụng bộ lọc thay thế : <p>The percentage is {{(100*count/total)| number:0}}%</p>, trong phần bình luận bên dưới.
Andrew Kuklewicz

2
Đối với Angular2, xác định một thành viên thành phần private Math = Math;và bạn có thể sử dụng nó.
Ondra ižka

Câu trả lời:


329

Bạn phải đưa Mathvào phạm vi của mình, nếu bạn cần sử dụng nó như $scopekhông biết gì về Toán học.

Cách đơn giản nhất, bạn có thể làm

$scope.Math = window.Math;

trong bộ điều khiển của bạn. Cách thức góc cạnh để làm điều này một cách chính xác sẽ là tạo ra một dịch vụ Toán học, tôi đoán vậy.


1
Cảm ơn. Tôi có một trường hợp sử dụng trong đó tôi có nhiều mẫu với logic hoàn toàn khác nhau và muốn cách ly chúng càng nhiều càng tốt. Hoàn hảo
Yablargo

48
Bạn nên sử dụng bộ lọc, không đặt đối tượng Math vào phạm vi.
Soviut

4
Điều này tốt cho những thứ nhanh chóng và bẩn thỉu; nhanh chóng chế nhạo một cái gì đó hoặc muốn xem một cái gì đó hoạt động như thế nào. Đó có lẽ là điều mà ai đó muốn làm toán trong các tệp mẫu html của họ muốn.
Lan

1
Sử dụng các hàm trong các ràng buộc sẽ làm cho hàm gọi trong bản cập nhật của từng phạm vi và sẽ sử dụng quá nhiều tài nguyên.
desmati

Giải pháp này là sai . tại sao? 1- Làm ô nhiễm phạm vi toàn cầu là ý tưởng tồi tệ nhất. 2- khung nhìn chịu trách nhiệm formatting, vì vậy sử dụng bộ lọc thay thế.
Afshin Mehrabani

304

Mặc dù câu trả lời được chấp nhận là đúng mà bạn có thể tiêm Mathđể sử dụng nó trong góc, nhưng đối với vấn đề cụ thể này, cách thông thường / góc hơn là bộ lọc số:

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

Bạn có thể đọc thêm về numberbộ lọc tại đây: http://docs.angularjs.org/api/ng/filter/number


7
Đây không chỉ là cách Angular, mà còn là cách chính xác. Đây là trách nhiệm của 'định dạng' giá trị.
Luke

39
Andrew, xin lỗi để làm lộn xộn bài viết của bạn với bình luận này. Câu trả lời của bạn là tốt và hữu ích; tuy nhiên, tôi muốn không đồng ý với các ý kiến ​​khác. Tôi thường co rúm người lại khi mọi người chào mời "con đường góc cạnh" như thể đó là một số yếu tố của sự phát triển hoàn hảo. Không phải vậy. OP đã hỏi chung về cách đưa các hàm toán học vào một ràng buộc, với làm tròn chỉ được trình bày như một ví dụ. Mặc dù câu trả lời này có thể là "cách thức góc cạnh" của những người theo chủ nghĩa thuần túy để giải quyết chính xác một vấn đề toán học, nhưng nó không trả lời đầy đủ câu hỏi.
kbrimington

1
Xin lỗi cho khảo cổ học, nhưng điều này không chính xác khi bạn cần một Số làm đầu ra. {{1234.567|number:2}}ám chỉ như 1,234.57hoặc 1 234,57. Nếu bạn cố gắng chuyển nó cho <input type="number" ng-value="1234.567|number:2">bạn, sẽ làm trống đầu vào, vì giá trị KHÔNG PHẢI LÀ SỐ ĐÚNG, nhưng đại diện chuỗi phụ thuộc cục bộ.
SWilk

61

Tôi nghĩ cách tốt nhất để làm điều đó là tạo một bộ lọc, như thế này:

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

sau đó đánh dấu trông như thế này:

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

Cập nhật fiddle: http://jsfiddle.net/BB4T4/


Bộ lọc số như được đưa ra trong câu trả lời của klaxon sẽ làm trần, vì vậy không cần điều đó.
Kim

Tôi đã làm một cái gì đó tương tự, chỉ sử dụng bộ lọc để tạo bộ đếm thời gian như tôi muốn. Nói cách khác, tôi lấy thời gian hiện tại và định dạng nó thành một chuỗi như tôi thấy phù hợp bằng cách sử dụng bộ lọc tùy chỉnh. Ngoài ra bộ lọc số là tốt khi bạn muốn làm tròn số của mình, nhưng đối với các chức năng sàn và trần thì vô dụng.
Gabriel Lovetro

37

Đây là một câu hỏi để trả lời, bởi vì bạn đã không đưa ra bối cảnh đầy đủ về những gì bạn đang làm. Câu trả lời được chấp nhận sẽ hoạt động, nhưng trong một số trường hợp sẽ gây ra hiệu suất kém. Điều đó, và sẽ khó kiểm tra hơn.

Nếu bạn đang làm điều này như là một phần của một hình thức tĩnh, tốt thôi. Câu trả lời được chấp nhận sẽ hoạt động, ngay cả khi nó không dễ kiểm tra và thật khó hiểu.

Nếu bạn muốn "Angular" về điều này:

Bạn sẽ muốn giữ bất kỳ "logic kinh doanh" nào (tức là logic làm thay đổi dữ liệu được hiển thị) khỏi tầm nhìn của bạn. Điều này là để bạn có thể đơn vị kiểm tra logic của mình và do đó bạn không kết hợp chặt chẽ bộ điều khiển và chế độ xem của mình. Về mặt lý thuyết, bạn sẽ có thể trỏ bộ điều khiển của mình vào một chế độ xem khác và sử dụng cùng các giá trị từ phạm vi. (nếu điều đó hợp lý).

Bạn cũng sẽ muốn xem xét rằng bất kỳ lệnh gọi hàm nào bên trong một ràng buộc (chẳng hạn như {{}}hoặc ng-bindhoặc ng-bind-html) sẽ phải được đánh giá trên mỗi thông báo , bởi vì góc không có cách nào để biết giá trị đã thay đổi hay không giống như với một thuộc tính trên phạm vi.

Cách "góc cạnh" để làm điều này sẽ là lưu trữ giá trị trong một thuộc tính trên phạm vi thay đổi bằng cách sử dụng sự kiện thay đổi ng hoặc thậm chí là đồng hồ $.

Ví dụ với dạng tĩnh:

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>

Và bây giờ bạn có thể kiểm tra nó!

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

bạn có thể biên dịch phần tử và sau đó kiểm tra giá trị tìm thấy trong phần tử dom. điều đó thực sự được ưa thích vì bạn cũng có thể đang sử dụng các bộ lọc nội địa hóa.
FlavorScape

Câu trả lời tốt. Nhưng tự hỏi tại sao phải chuẩn bị trước $windowvì nó dường như chỉ hoạt động với kế hoạch Math.round()?
Neel

3
@Neel - $ window cho phép bạn dễ dàng chế giễu các Mathbài kiểm tra hơn. Thay vào đó, bạn có thể tạo một dịch vụ Toán học và tiêm nó.
Ben Lesh

8

Tại sao không bọc toàn bộ obj toán học trong một bộ lọc?

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


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

        return 0;
    }
}

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

và sử dụng:

{{số_var | toán học: 'trần'}}


8

Tùy chọn tốt hơn là sử dụng:

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

Nó đặt giá trị mặc định của phương trình thành 0 trong trường hợp khi các giá trị không được khởi tạo.



6

Cách dễ nhất để thực hiện phép toán đơn giản với Angular là trực tiếp trong đánh dấu HTML cho các ràng buộc riêng lẻ khi cần, giả sử bạn không cần phải tính toán hàng loạt trên trang của mình. Đây là một ví dụ:

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

Trong trường hợp này, bạn chỉ cần làm toán trong () và sau đó sử dụng bộ lọc | để có được một câu trả lời số. Dưới đây là thông tin thêm về định dạng Số góc dưới dạng văn bản:

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


4

Hoặc liên kết đối tượng Math toàn cầu vào phạm vi (nhớ sử dụng $ window chứ không phải window)

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

Sử dụng ràng buộc trong HTML của bạn:

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

Hoặc tạo bộ lọc cho hàm Math cụ thể mà bạn theo đuổi:

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

Sử dụng bộ lọc trong HTML của bạn:

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

2

Điều đó không giống như một cách rất góc cạnh để làm điều đó. Tôi không hoàn toàn chắc chắn tại sao nó không hoạt động, nhưng có lẽ bạn cần truy cập vào phạm vi để sử dụng một chức năng như vậy.

Đề nghị của tôi sẽ là tạo ra một bộ lọc. Đó là cách Angular.

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

Sau đó, trong HTML của bạn làm điều này:

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

1

Bộ numberlọc định dạng số với hàng nghìn dấu phân cách, vì vậy đây không hoàn toàn là một hàm toán học.

Hơn nữa, số thập phân 'giới hạn' của nó không ceilcó bất kỳ số thập phân nào được cắt nhỏ (vì một số câu trả lời khác sẽ khiến bạn tin), mà là sử dụng roundchúng.

Vì vậy, đối với bất kỳ hàm toán học nào bạn muốn, bạn có thể tiêm nó (dễ giả hơn là tiêm toàn bộ đối tượng Math) như vậy:

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

Không cần phải bọc nó trong một chức năng khác.


0

Đây ít nhiều là một bản tóm tắt của ba câu trả lời (của Sara Inés Calderón, klaxon và Gothburz), nhưng vì tất cả chúng đều thêm một cái gì đó quan trọng, tôi cho rằng nó đáng để tham gia các giải pháp và thêm một số giải thích.

Xem xét ví dụ của bạn, bạn có thể thực hiện các phép tính trong mẫu của mình bằng cách sử dụng:

{{ 100 * (count/total) }}

Tuy nhiên, điều này có thể dẫn đến rất nhiều vị trí thập phân, vì vậy sử dụng bộ lọc là một cách tốt để đi:

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

Theo mặc định, bộ lọc số sẽ để lại tối đa ba chữ số phân số , đây là nơi đối số phân sốkích thước khá tiện dụng ( {{ 100 * (count/total) | number:fractionSize }}), trong trường hợp của bạn sẽ là:

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

Điều này cũng sẽ làm tròn kết quả rồi:

Điều cuối cùng cần đề cập, nếu bạn dựa vào nguồn dữ liệu bên ngoài, có lẽ nên cung cấp giá trị dự phòng phù hợp (nếu không bạn có thể thấy NaN hoặc không có gì trên trang web của mình):

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

Sidenote: Tùy thuộc vào thông số kỹ thuật của bạn, bạn thậm chí có thể chính xác hơn với các dự phòng / xác định dự phòng ở các cấp thấp hơn (ví dụ {{(100 * (count || 10)/ (total || 100) | number:2)}}). Mặc dù, điều này có thể không phải lúc nào cũng có ý nghĩa ..


0

Ví dụ kiểu chữ góc bằng cách sử dụng một đường ống.

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

Thêm vào khai báo @NgModule

@NgModule({
  declarations: [
    MathPipe,

sau đó sử dụng trong mẫu của bạn như vậy:

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

TypeError: Math [args] không phải là một hàm
MattEnth
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.