web-dev-qa-db-fra.com

Fonctions mathématiques dans les liaisons AngularJS

Est-il possible d'utiliser des fonctions mathématiques dans les liaisons AngularJS?

par exemple. 

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

Ce violon montre le problème

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

221
ricick

Vous devez injecter Math dans votre oscilloscope, si vous devez l’utiliser en tant que $scope ne rien connaître de Math.

Manière la plus simple, vous pouvez faire

$scope.Math = window.Math;

dans votre contrôleur . Une façon angulaire de le faire correctement serait de créer un service mathématique, je suppose.

319
Tosh

Bien que la réponse acceptée soit correcte, vous pouvez injecter Math pour l’utiliser en mode angulaire, mais pour ce problème particulier, la méthode la plus classique/angulaire est le filtre numérique:

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

Vous pouvez en savoir plus sur le filtre number ici: http://docs.angularjs.org/api/ng/filter/number

298
Andrew Kuklewicz

Je pense que la meilleure façon de le faire est de créer un filtre, comme ceci: 

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

alors le balisage ressemble à ceci:

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

Violon mis à jour: http://jsfiddle.net/BB4T4/

61
sabinstefanescu

C’est une question poilue à laquelle répondre, car vous n’avez pas donné le contexte complet de ce que vous faites. La réponse acceptée fonctionnera, mais dans certains cas, les performances seront médiocres. Ça, et ça va être plus difficile à tester.

Si vous faites cela dans le cadre d'une forme statique, c'est bien. La réponse acceptée fonctionnera, même si ce n’est pas facile à tester, et c’est grave.

Si vous voulez être "angulaire" à ce sujet:

Vous voudrez conserver toute "logique métier" (c'est-à-dire une logique modifiant l'affichage des données) dans vos vues. Cela vous permet de tester votre logique et d'éviter de coupler étroitement votre contrôleur et votre vue. Théoriquement, vous devriez pouvoir pointer votre contrôleur vers une autre vue et utiliser les mêmes valeurs à partir des étendues. (si ça a du sens).

Vous voudrez également considérer que tous les appels de fonction à l'intérieur d'une liaison (tels que {{}} ou ng-bind ou ng-bind-html) devront être évalués sur every digest , car angular n'a aucun moyen de savoir si la valeur a changé ou pas comme il le ferait avec une propriété sur la portée.

La méthode "angulaire" consiste à mettre en cache la valeur d'une propriété dans l'étendue de la modification à l'aide d'un événement ng-change ou même d'une surveillance $.

Par exemple avec une forme statique:

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>

Et maintenant, vous pouvez le tester!

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

Une meilleure option consiste à utiliser:

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

Il définit la valeur par défaut de l'équation sur 0 dans le cas où les valeurs ne sont pas initialisées.

8
klaxon

Pourquoi ne pas envelopper l'objet mathématique entier dans un filtre?

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


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

        return 0;
    }
}

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

et à utiliser:

{{number_var | maths: 'ceil'}}

7
Marcos Ammon

Le moyen le plus simple de faire des calculs simples avec Angular est directement dans le balisage HTML pour les liaisons individuelles en fonction des besoins, en supposant que vous n’ayez pas besoin de faire des calculs en masse sur votre page. Voici un exemple:

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

Dans ce cas, il vous suffit de faire le calcul dans la (), puis d'utiliser un filtre | pour obtenir un numéro de réponse. Voici plus d'informations sur le formatage des nombres angulaires en texte: 

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

6

Si vous cherchez à faire un tour simple en angulaire, vous pouvez facilement définir le filtre dans votre expression. Par exemple:

{{ val | number:0 }}

Voir ceci CodePen example & pour les autres options de filtrage de numéros.

Docs angulaires sur l’utilisation des filtres numériques

5
Gothburz

Soit lier l’objet mathématique global à l’étendue (souvenez-vous d’utiliser $ window not window)

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

Utilisez la liaison dans votre code HTML:

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

Ou créez un filtre pour la fonction mathématique spécifique que vous recherchez:

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

Utilisez le filtre dans votre code HTML:

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

Cela ne ressemble pas à une façon très angulaire de le faire. Je ne suis pas tout à fait sûr pourquoi cela ne fonctionne pas, mais vous auriez probablement besoin d'accéder à la portée pour pouvoir utiliser une telle fonction.

Ma suggestion serait de créer un filtre. C'est la manière angulaire.

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

Ensuite, dans votre code HTML, procédez comme suit:

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

Exemple de TypeScript angulaire utilisant un tuyau.

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

Ajouter aux déclarations @NgModule 

@NgModule({
  declarations: [
    MathPipe,

puis utilisez dans votre modèle comme suit:

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

Le filtre number formate le nombre avec des séparateurs de milliers, de sorte qu'il ne s'agit pas strictement d'une fonction mathématique.

De plus, son «limiteur» décimal ne ceil pas de décimales hachées (comme le suggèrent certaines autres réponses), mais plutôt rounds.

Donc, pour n'importe quelle fonction mathématique que vous voulez, vous pouvez l'injecter (plus facile à simuler que d'injecter tout l'objet Math) comme ceci:

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

Pas besoin de l'envelopper dans une autre fonction non plus.

0
user3638471