Is there a way to use math functions in angular bindings?
e.g.
<p>The percentage is {{Math.round(100*count/total)}}%</p>
This fiddle shows the problem
While the accepted answer is right that you can inject Math
to use it in angular, for this particular problem, the more conventional/angular way is the number filter:
<p>The percentage is {{(100*count/total)| number:0}}%</p>
You can read more about the number
filter here: http://docs.angularjs.org/api/ng/filter/number
You have to inject Math
into your scope, if you need to use it as
$scope
know nothing about Math.
Simplest way, you can do
$scope.Math = window.Math;
in your controller. Angular way to do this correctly would be create a Math service, I guess.
This is a hairy one to answer, because you didn't give the full context of what you're doing. The accepted answer will work, but in some cases will cause poor performance. That, and it's going to be harder to test.
If you're doing this as part of a static form, fine. The accepted answer will work, even if it isn't easy to test, and it's hinky.
You'll want to keep any "business logic" (i.e. logic that alters data to be displayed) out of your views. This is so you can unit test your logic, and so you don't end up tightly coupling your controller and your view. Theoretically, you should be able to point your controller at another view and use the same values from the scopes. (if that makes sense).
You'll also want to consider that any function calls inside of a binding (such as {{}}
or ng-bind
or ng-bind-html
) will have to be evaluated on every digest, because angular has no way of knowing if the value has changed or not like it would with a property on the scope.
The "angular" way to do this would be to cache the value in a property on the scope on change using an ng-change event or even a $watch.
For example with a static form:
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.
});
});
I think the best way to do it is by creating a filter, like this:
myModule.filter('ceil', function() {
return function(input) {
return Math.ceil(input);
};
});
then the markup looks like this:
<p>The percentage is {{ (100*count/total) | ceil }}%</p>
Updated fiddle: http://jsfiddle.net/BB4T4/
Either bind the global Math object onto the scope (remember to use $window not window)
$scope.abs = $window.Math.abs;
Use the binding in your HTML:
<p>Distance from zero: {{abs(distance)}}</p>
Or create a filter for the specific Math function you're after:
module.filter('abs', ['$window', function($window) {
return function(n) {
return $window.Math.abs($window.parseInt(n));
};
});
Use the filter in your HTML:
<p>Distance from zero: {{distance | abs}}</p>