Математические функции в привязках AngularJS

198

Есть ли способ использовать математические функции в привязках 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> , больше в комментарии ниже.
  • 1
    Для Angular2 определите член компонента private Math = Math; и вы можете использовать это.
Теги:

12 ответов

303
Лучший ответ

Вам нужно ввести Math в свою область действия, если вам нужно использовать ее как $scope ничего не знает о математике.

Простейший способ, вы можете сделать

$scope.Math = window.Math;

в вашем контроллере. Angular, чтобы сделать это правильно, я создаю Math-сервис.

  • 1
    Благодарю. У меня есть сценарий использования, когда у меня есть несколько шаблонов с совершенно другой логикой, и я хочу максимально изолировать их. Отлично
  • 48
    Вы должны использовать фильтр, а не помещать объект Math в область видимости.
Показать ещё 4 комментария
297

В то время как принятый ответ прав, что вы можете ввести Math, чтобы использовать его в angular, для этой конкретной проблемы более обычным способом /angular является числовой фильтр:

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

Подробнее о фильтре number вы можете прочитать здесь: http://docs.angularjs.org/api/ng/filter/number

  • 16
    Это угловой путь ..
  • 7
    Это не только угловой путь, это правильный путь. Ответственность за «форматирование» значения лежит на представлении.
Показать ещё 2 комментария
52

Я думаю, что лучший способ сделать это - создать фильтр, например:

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

тогда разметка выглядит следующим образом:

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

Обновлен скрипт: http://jsfiddle.net/BB4T4/

  • 0
    Числовой фильтр, приведенный в ответе Клаксона, уже будет работать, поэтому в этом нет необходимости.
  • 0
    Я сделал нечто подобное, только используя фильтр, чтобы создать сам таймер, как я хотел. Другими словами, я бы взял текущее время и отформатировал бы его в строку, как я счел нужным, используя пользовательский фильтр. Кроме того, числовой фильтр хорош для округления чисел, но для функций пола и потолка он бесполезен.
32

Это волосатый ответ, потому что вы не дали полного контекста того, что вы делаете. Принятый ответ будет работать, но в некоторых случаях это приведет к низкой производительности. Это, и это будет труднее проверить.

Если вы делаете это как часть статической формы, отлично. Принятый ответ будет работать, даже если его нелегко проверить, и это неловко.

Если вы хотите быть "Angular" об этом:

Вы хотите сохранить любую "бизнес-логику" (то есть логику, которая изменяет отображаемые данные) из ваших представлений. Это значит, что вы можете unit test использовать свою логику, и поэтому вы не будете плотно связывать ваш контроллер и ваше представление. Теоретически, вы должны указывать ваш контроллер на другое представление и использовать те же значения из областей. (если это имеет смысл).

Вы также захотите рассмотреть, что любые вызовы функций внутри привязки (например, {{}} или ng-bind или ng-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.
  });
});
  • 0
    Вы можете скомпилировать элемент и затем проверить значение, найденное в элементе dom. это на самом деле предпочтительнее, поскольку вы также можете использовать фильтры локализации.
  • 0
    Хороший ответ. Но интересно, зачем добавлять $window так как он, кажется, работает только с планом Math.round() ?
Показать ещё 1 комментарий
6

Самый простой способ сделать простую математику с помощью Angular - это непосредственно в разметке HTML для отдельных привязок по мере необходимости, предполагая, что вам не нужно делать массовые вычисления на своей странице. Вот пример:

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

В этом случае вы просто выполните математику в(), а затем используйте фильтр | чтобы получить номер ответа. Здесь больше информации о форматировании чисел Angular в виде текста:

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

6

Лучше использовать:

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

Он устанавливает значение по умолчанию для уравнения равным 0 в случае, когда значения не инициализируются.

5

Почему бы не обернуть всю математику 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'}}

5

Если вы хотите сделать простой раунд в Angular, вы можете легко установить фильтр внутри своего выражения. Например:

{{ val | number:0 }}

Смотрите этот CodePen пример и для других параметров фильтра числа.

Angular Документы об использовании цифровых фильтров

3

Либо привязать глобальный объект Math к области (не забудьте использовать $window not 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 сделать это. Я не совсем уверен, почему он не работает, но вам, вероятно, потребуется получить доступ к области, чтобы использовать такую ​​функцию.

Мое предложение было бы создать фильтр. Это способ Angular.

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

Затем в вашем HTML сделайте следующее:

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

Это более или менее резюме трех ответов (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 }}

Sidenote: в зависимости от ваших спецификаций вы даже можете быть более точным с резервными/резервными резервами на более низких уровнях (например, {{(100 * (count || 10)/ (total || 100) | number:2)}}). Хотя, это может не всегда иметь смысл.

0

Фильтр number форматирует число с разделителями тысяч, поэтому это не строго математическая функция.

Кроме того, его десятичный "лимитер" не ceil не нарезал десятичные числа (как некоторые другие ответы заставляют вас поверить), а скорее round их.

Итак, для любой математической функции, которую вы хотите, вы можете ввести ее (проще обмануть, чем впрыснуть весь объект Math) следующим образом:

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

Не нужно также обертывать его другой функцией.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню