Математичні функції в прив'язках 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.