Is there something similar to AngularJS $interpolate in Angular2+?
I would like to achieve this (in JS/TS code) but with Angular2+:
EDIT
The main goal to achieve is to $interpolate string without having
knowledge of transform pipe which is used inside string to
interpolate.
JS
var myApp = angular.module('myApp',[]);
myApp.controller('myCtrl', myCtrl);
var JsonTranslation = {
"txt1": "Cash {{ value | currency: 'USD'}}",
"txt2": "Number {{ value | number: 3}}"
};
function myCtrl($scope, $interpolate) {
$scope.txt1 = $interpolate(JsonTranslation.txt1)({value: 50000});
$scope.txt2 = $interpolate(JsonTranslation.txt2)({value: 50000.123456});
}
HTML
<div ng-controller="myCtrl">
<div>{{txt1}}</div> <!-- Cash USD50,000.00 -->
<div>{{txt2}}</div> <!-- Number 50,000.123 -->
</div>
Working jsfiddle http://jsfiddle.net/3nf7cza4/
Yes:
`Cash ${this.currency.transform(value, 'USD')}`
Where this.currency is the injected pipe you want to use and transform is the function in the pipe which converts your value.
I assume currency is an angular pipe (can't remember if it is or not) so you'd simply inject it like so:
import {CurrencyPipe} from '#angular/core';
constructor(private currency: CurrencyPipe) {}
I have a directive with a template that looks like:
<span> {{foo.bar}} {{foo.car}} {{foo.dar}}</span>
which is called in multiple locations. The way it is called is:
<foo-dir foo="fooData"></foo-dir>
Is there any way to apply a custom filter in the html directive tag and not in the directive itself?
I'm hoping something exists such as:
ng-innertext-filter="filterName:params"
but it may be wishful thinking.
If that is not possible, is there a way to use the filter in the directive controller without having to apply it everywhere the directive is used?
Thanks.
Edit: filter code:
import angular from 'angular';
function someFilter() {
return (input, filterParam) => {
if (input && filterParam && input.length)
if(filterParam)
return input.filterLogic();
return input;
}
}
The filter works fine when in a templated string {{somedata | someFilter}}. But I am looking for a way to apply the filter to the directive tag itself.
To do exactly what you want:
use the filter on the resulting string from a directive in the html tag
you need to create a template string and recompile your DOM.
This is a quite advanced use of AngularJS. I hope you enjoy it :-)
var app = angular.module("app", []);
app.controller('ctrl', function ($scope) {
$scope.foo = {
bar: "1",
car: "2",
dar: "3"
};
});
app.directive('foodir', function ($compile) {
return {
restrict: 'E',
scope: {
foo: '=', // Bind the variable to the directive's scope
filter: '#' // Just get the string for compilation
},
link: function ($scope, $element, $attrs) {
// This is the string on which you may want to apply a filter
var string_template = '" bar:" + foo.bar + " car:" + foo.car + " dar:" + foo.dar';
// Full string to compile without filter
var html = '<span>{{' + string_template + '}}</span>';
if ($scope.filter !== undefined)
// Full string to compile with filter (optional)
html = '<span>{{' + string_template + ' | '+$scope.filter+'}}</span>';
console.log(html);
var e = $compile(html)($scope); // Convert your string to a DOM element
$element.replaceWith(e); // Make it the content of the directive
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="app" ng-controller="ctrl">
<foodir foo="foo"></foodir>
<hr/>
<foodir foo="foo" filter="uppercase"></foodir>
<hr/>
<foodir foo="foo" filter="limitTo:10"></foodir>
</body>
Now, if you just want to apply a filter to some parts of your template (and not the full string), you can avoid the string_template and use basic interpolation like this:
var html = '<span> {{foo.bar}} {{foo.car}} {{foo.dar |' + $scope.filter + '}}</span>'
I'm not sure what the first part of your question means. If you're wanting to apply this filter to the compiled result of a directive, I'm not sure you can. I can help with using a filter in a controller, though.
If you define a custom AngularJS filter like so:
app.filter('makeItPretty', function() {
return function(value) {
return value.toUpperCase();
};
});
Then you can access your filter from within a controller by injecting the built-in $filter service:
app.controller('MyController', function($filter) {
var makeItPretty = $filter('makeItPretty');
var myPrettyText = makeItPretty('tHiS iS UgLy!');
});
I am trying to build a filter based on this post. I want to be able to switch from one currency to the other by switching a select input with an ng-click on it, but I'm failing to get the input into the filter.
My html is:
<div ng-controller="myCtrl">
you have, {{money | currency}}...<br><br>
</div>
<select>
<option ng-click="currencySymbol = 'USD'; currencyRate=exchange.usd.rate">USD<option>
<option ng-click="currencySymbol = '£'; currencyRate=exchange.pound.rate">£<option>
<option ng-click="currencySymbol = '€'; currencyRate=exchange.euro.rate">€<option>
</select>
And the Angular part:
angular
.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.money = 100;
$scope.exchange = [
{"usd":{"rate":1}},
{"pound":{"rate":0.702846}},
{"euro":{"rate":0.885055}}
];
})
.filter('currency', function() {
var defaultCurrency = 'USD';
return function(input, currencySymbol, currencyRate) {
var out = "";
currencySymbol = currencySymbol || defaultCurrency;
currencyRate = currencyRate || 1.00;
out = input * currencyRate;
return out + ' ' + currencySymbol;
}
});
You can check the JsFiddle here
Thanks in advance!
Quite a few updates here:
First off, your dropdown is located outside of the controller
You could use the ng-options directive to clean up your select menu
You have ng-click on each option which doesn't work well with the complex model you are trying to maintain
If you pass the whole selected option into the filter, you can manage everything from that one scope variable keeping the data in one place
Here is a working example
http://jsfiddle.net/r0m4n/dLLtzqyr/1/
This will be your simplified html:
<div ng-controller="myCtrl">
you have, {{money | currency: selectedCurrency}}...
<br>
<br>
<select ng-model="selectedCurrency" ng-options="item as item.label for item in exchange">
</select>
</div>
And your js:
angular
.module('myApp', [])
// controller here
.controller('myCtrl', function($scope) {
$scope.money = 100;
$scope.exchange = [{
label: "USD",
rate: 1
}, {
label: "£",
rate: 0.702846
}, {
label: "€",
rate: 0.885055
}];
$scope.selectedCurrency = $scope.exchange[0];
})
.filter('currency', function() {
return function(input, selectedCurrency) {
var out = "";
out = input * selectedCurrency.rate;
return out + ' ' + selectedCurrency.label;
}
});
You define 3 parameter in your filter currency. Whenever you use your filter, you need to give these parameters to your filter. In your case :
<div ng-controller="myCtrl">
you have, {{money | currency:currencySymbol:exchange[2].euro.rate}}...<br><br>
</div>
In AngularJS world, you call your filter after the | and you use : as a separator for parameters (the first parameter input is always given implicitly).
To make the debug easier, you can add this in your filter :
return function(input, currencySymbol, currencyRate) {
console.info('currency parameter', input, currencySymbol, currencyRate);
Please note that I hardcoded the parameter currencyRate because it's not handy to retrieve with the way you store your array.
I have read about the module way of defining a filter:
myApp.filter('filtername', function() {
return function() {
};
});
But what if I am using a simple function controller for my angular app and not as a module?
I.e. all I have at the top of my html is:
<html ng-app ng-controller="MyApp">
and then I include a js file with a simple controller:
function MyApp($scope, $http){
}
but I need to define a custom filter - in my particular case I'm trying to order an ngRepeat using Justin Klemm's object ordering filter. So how can I define a filter not using the module style syntax?
If you want create a new filter in your app you could define the your custom filter like below:
in your js
angular.module("yourModule")
.filter("yourFilretName",function(){
return function(value,arg1,arg2 .....){
var result = [];
// filter processing the object that pass the filtering, must be push on the result array
return result;
}
})
in your template
<your-tag ng-repeat="book in bookList | yourFilretName :arg1:agr2:....:argn>
.....
</your-tag>
<div ng-app="myApp" ng-controller="myCtrl">
<ul>
<li ng-repeat="item in items | filter: myFilter">{{ item }}</li>
</ul>
...
var myApp= angular.module("myApp", []);
myApp.controller('myCtrl', function($scope ) {
function myFilter(item) {
return item == "some condition"
}
})
Is there a way to use math functions in AngularJS bindings?
e.g.
<p>The percentage is {{Math.round(100*count/total)}}%</p>
This fiddle shows the problem
http://jsfiddle.net/ricick/jtA99/1/
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.
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
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/
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.
If you want to be "Angular" about this:
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>
And now you can test it!
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.
});
});
Why not wrap the whole math obj in a filter?
var app = angular.module('fMathFilters',[]);
function math() {
return function(input,arg) {
if(input) {
return Math[arg](input);
}
return 0;
}
}
return app.filter('math',[math]);
and to use:
{{ number_var | math:'ceil'}}
Better option is to use :
{{(100*score/questionCounter) || 0 | number:0}}
It sets default value of equation to 0 in the case when values are not initialized.
If you're looking to do a simple round in Angular you can easily set the filter inside your expression. For example:
{{ val | number:0 }}
See this CodePen example & for other number filter options.
Angular Docs on using Number Filters
The easiest way to do simple math with Angular is directly in the HTML markup for individual bindings as needed, assuming you don't need to do mass calculations on your page. Here's an example:
{{(data.input/data.input2)| number}}
In this case you just do the math in the () and then use a filter | to get a number answer. Here's more info on formatting Angular numbers as text:
https://docs.angularjs.org/api/ng/filter
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>
That doesn't look like a very Angular way of doing it. I'm not entirely sure why it doesn't work, but you'd probably need to access the scope in order to use a function like that.
My suggestion would be to create a filter. That's the Angular way.
myModule.filter('ceil', function() {
return function(input) {
return Math.ceil(input);
};
});
Then in your HTML do this:
<p>The percentage is {{ (100*count/total) | ceil }}%</p>
The number filter formats the number with thousands separators, so it's not strictly a math function.
Moreover, its decimal 'limiter' doesn't ceil any chopped decimals (as some other answers would lead you to believe), but rather rounding them.
So for any math function you want, you can inject it (easier to mock than injecting the whole Math object) like so:
myModule.filter('ceil', function () {
return Math.ceil;
});
No need to wrap it in another function either.
This is more or less a summary of three answers (by Sara Inés Calderón, klaxon and Gothburz), but as they all added something important, I consider it worth joining the solutions and adding some more explanation.
Considering your example, you can do calculations in your template using:
{{ 100 * (count/total) }}
However, this may result in a whole lot of decimal places, so using filters is a good way to go:
{{ 100 * (count/total) | number }}
By default, the number filter will leave up to three fractional digits, this is where the fractionSize argument comes in quite handy
({{ 100 * (count/total) | number:fractionSize }}), which in your case would be:
{{ 100 * (count/total) | number:0 }}
This will also round the result already:
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>
Last thing to mention, if you rely on an external data source, it probably is good practise to provide a proper fallback value (otherwise you may see NaN or nothing on your site):
{{ (100 * (count/total) | number:0) || 0 }}
Sidenote: Depending on your specifications, you may even be able to be more precise with your fallbacks/define fallbacks on lower levels already (e.g. {{(100 * (count || 10)/ (total || 100) | number:2)}}). Though, this may not not always make sense..
Angular Typescript example using a pipe.
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;
}
}
Add to #NgModule declarations
#NgModule({
declarations: [
MathPipe,
then use in your template like so:
{{(100*count/total) | math:'round'}}