Математичні функції в прив'язках 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>детальніше в коментарі нижче.
Андрій Куклевич

2
Для Angular2 визначте елемент компонента private Math = Math;і ви можете ним скористатися.
Ondra Žižka

Відповіді:


329

Ви повинні вводити Mathв свою область, якщо вам потрібно використовувати її, оскільки $scopeнічого не знаєте про Math.

Найпростіший спосіб, який ви можете зробити

$scope.Math = window.Math;

у вашому контролері. Кутовим способом зробити це правильно було б створити сервіс Math, я думаю.


1
Дякую. У мене є випадок використання, коли у мене є кілька шаблонів з абсолютно різною логікою і хочу їх максимально ізолювати. Ідеально
Ябларго

48
Ви повинні використовувати фільтр, а не ставити об'єкт Math у область застосування.
Радянський

4
Це добре для швидких і брудних речей; швидко знущаючись над чимось або бажаючи побачити, як щось працює. Що, мабуть, хоче той, хто хоче займатися математикою у своїх файлах HTML-шаблонів.
Лан

1
Використання функцій у прив'язці зробить функцію для виклику в оновлення кожної області та використовуватиме забагато ресурсів.
десмати

Це рішення неправильне . чому? 1- Забруднення глобальної сфери - це найгірша ідея коли-небудь. За них покладено 2–2 перегляди formatting, тому замість цього використовуйте фільтр.
Афшин Мехрабані

304

Хоча прийнята відповідь правильна, що ви можете ввести Mathїї в кутовій, для цієї конкретної проблеми більш звичайним / кутовим способом є фільтр чисел:

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

Докладніше про numberфільтр можна прочитати тут: http://docs.angularjs.org/api/ng/filter/number


7
Це не тільки кутовий шлях, це правильний шлях. Це відповідальність перегляду за "форматування" значення.
Лука

39
Ендрю, вибачте, захаращуючи ваш коментар цим коментарем. Ваша відповідь хороша і корисна; однак я хочу не погодитися з іншими коментарями. Я часто стискаюся, коли люди вимовляють "кутовий шлях" так, ніби це якийсь парагон ідеального розвитку. Це не так. ОП запитувала взагалі, як включити математичні функції у прив'язку, при цьому округлення подано лише як приклад. Хоча пуристи ця відповідь може бути "кутовим шляхом" для вирішення саме однієї математичної задачі, вона не відповідає повністю на питання.
kbrimington

1
Вибачте за археологію, але це неправильно, коли вам потрібен номер як вихід. {{1234.567|number:2}}надає як 1,234.57або 1 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/


Фільтр чисел, вказаний у відповіді klaxon, зробить потолок уже, тому в цьому немає необхідності.
Кім

Я робив щось подібне, лише використовуючи фільтр, щоб створити сам таймер, як я цього хотів. Іншими словами, я б взяв поточний час і відформатувати його в рядок, як вважаю за потрібне, використовуючи спеціальний фільтр. Також фільтр чисел хороший, коли ви хочете округлити свої цифри, але для функцій підлоги та стелі він марний.
Габріель Ловетро

37

На це відповісти волохатий, бо ви не дали повного контексту того, що робите. Прийнята відповідь спрацює, але в деяких випадках спричинить низьку ефективність. Це, і це буде важче перевірити.

Якщо ви робите це як частина статичної форми, добре. Прийнята відповідь спрацює, навіть якщо це непросто перевірити, і це химерно.

Якщо ви хочете бути "кутовим" з цього приводу:

Ви хочете, щоб будь-яка "бізнес-логіка" (тобто логіка, яка змінює дані, що відображаються), поза вашими поглядами. Це так, що ви можете перевірити свою логіку, і щоб ви не закінчилися міцно з’єднати контролер і свій погляд. Теоретично, ви повинні мати змогу вказувати контролеру на інший вигляд і використовувати ті самі значення з областей. (якщо це має сенс).

Ви також хочете врахувати, що будь-які виклики функцій всередині прив'язки (наприклад, {{}}або ng-bindабо ng-bind-html) повинні бути оцінені під час кожного дайджесту , оскільки кутовий не має можливості знати, змінилося чи не так, як це було б у властивості про сферу застосування.

"Кутовим" способом зробити це було б кешування значення у властивості в області застосування при зміні за допомогою події зміни ng або навіть $ 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

Хороша відповідь. Але цікаво, чому передувати, $windowоскільки, здається, працює з просто планом Math.round()?
Ніл

3
@Neel - $ window дозволяє легше знущатися над Mathтестами. Альтернативно, ви можете створити службу Math та ввести її.
Бен Леш

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]);

і використовувати:

{{номер_вар | математика: 'стеля'}}


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 not 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

Це не схоже на дуже кутовий спосіб зробити це. Я не зовсім впевнений, чому це не працює, але вам, мабуть, знадобиться отримати доступ до області, щоб використовувати таку функцію.

Моєю пропозицією було б створити фільтр. Це кутовий шлях.

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

Це більш-менш резюме трьох відповідей (Сара Інес Калдерон, Клаксон та Готбурц), але оскільки всі вони додали щось важливе, я вважаю, що варто приєднатись до рішень та додати ще кілька пояснень.

Розглядаючи ваш приклад, ви можете робити розрахунки у своєму шаблоні, використовуючи:

{{ 100 * (count/total) }}

Однак це може призвести до безлічі десяткових знаків, тому використання фільтрів - хороший спосіб:

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

За замовчуванням фільтр чисел залишить до трьох дробових цифр. Ось тут досить зручним є аргумент дробSize ( {{ 100 * (count/total) | number:fractionSize }}), що у вашому випадку буде:

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

Це також окружить результат вже:

Останнє, що потрібно зазначити, якщо ви покладаєтесь на зовнішнє джерело даних, можливо, це хороша практика забезпечити належне резервне значення (інакше на вашому веб-сайті ви можете побачити NaN або нічого):

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

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


0

Приклад кутового шрифту з використанням труби.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.