Here is the code on plnkr.co

or here:

"use strict"; angular .module("exampleApp", []) .factory('currency', currencyFactory) .factory("User", userFactory) .filter("currency", currencyFilter) .controller("BaseCtrl", BaseCtrl); function currencyFactory() { var ticker; const currency = {}; currency.getTicker = function() { ticker = { "USD": { "buy": 1, "sell": 1, "symbol": "$", "decimalPlaces": 3, "name": "US Dollar", "code": "USD" }, "EUR": { "buy": 0.885, "sell": 0.885, "symbol": "€", "decimalPlaces": 3, "name": "Euro", "code": "EUR" } } // FakeRest return ticker; } currency.convertToUsd = function(amount, currency) { if (amount == 0) return 0; let cur = ticker[currency]; if (amount == null || currency == null || !cur) return null; return (amount / cur.sell) }; currency.convertFromUsd = function(amount, currency) { if (amount == 0) return 0; let cur = ticker[currency]; if (amount == null || currency == null || !cur) return null; return (amount * cur.buy) }; currency.formatForView = function(amount, currency) { let cur = ticker[currency]; if (!cur) return null; return cur.symbol + amount.toFixed(cur.decimalPlaces); }; currency.getList = function() { var array = []; console.log(ticker); angular.forEach(ticker, function(value) { array.push({ code: value.code, name: value.name }); }); return array; }; currency.getTicker(); return currency; } function userFactory() { const user = { settings: { currency: "USD" } } user.change = function(to) { user.settings.currency = to; } return user; } currencyFilter.$inject = ["currency", "User"]; function currencyFilter(currencyFactory, User) { return function(amount, display = true, currency = User.settings.currency) { let converted = currencyFactory.convertFromUsd(amount, currency); return display ? currencyFactory.formatForView(converted, currency) : converted; } } function BaseCtrl($scope, User, currency) { $scope.currencies = currency.getList(); $scope.current = User.settings.currency; $scope.change = function(to) { User.settings.currency = to; } } 
 <!DOCTYPE html> <html ng-app="exampleApp"> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script> <link rel="stylesheet" href="style.css" /> <script src="script.js"></script> </head> <body ng-controller="BaseCtrl"> <h1>{{ 100.75 | currency }}</h1> <select ng-model="current"> <option ng-repeat="currency in currencies" value="{{ currency.code }}">{{ currency.name }}</option> </select> </body> </html> 

How can I make the filter value update when User.settings.currency changes? In other words when changing currency?

UPD: If you assign parameters to the filter, it will certainly work. But how to add dynamism directly from the filter, such as in Angular Translate, where I can write {{ "key" | translate }} {{ "key" | translate }} and when changing the language value will change itself?

  • for select it is better to use ng-options instead of ng-repeat - Grundy
  • well, the directive is not used anywhere, apparently instead of the directive you wanted to filter - Grundy
  • @Grundy, right, I'm confused. - blits
  • so it is necessary to declare as a filter, and not as a directive, I indicated above what in the comments :) - Grundy
  • @Grundy, updated the question, look, does not work anyway :( - blits

1 answer 1

The problem is that in the place that displays the final value nothing changes

 <h1>{{ 100.75 | currency }}</h1> 

In this case, the filter is always called with the same parameter, and accordingly returns the same value.

To add dynamism, you need to pass to the filter the currency parameter to translate:

 100.75 | currency:current 

Thus, when current changed, changes will current wherever it is used.

Example:

 "use strict"; angular .module("exampleApp", []) .factory('currency', currencyFactory) .factory("User", userFactory) .filter("currency", currencyFilter) .controller("BaseCtrl", BaseCtrl); function currencyFactory() { var ticker; const currency = {}; currency.getTicker = function() { ticker = { "USD": { "buy": 1, "sell": 1, "symbol": "$", "decimalPlaces": 3, "name": "US Dollar", "code": "USD" }, "EUR": { "buy": 0.885, "sell": 0.885, "symbol": "€", "decimalPlaces": 3, "name": "Euro", "code": "EUR" } } // FakeRest return ticker; } currency.convertToUsd = function(amount, currency) { if (amount == 0) return 0; let cur = ticker[currency]; if (amount == null || currency == null || !cur) return null; return (amount / cur.sell) }; currency.convertFromUsd = function(amount, currency) { if (amount == 0) return 0; let cur = ticker[currency]; if (amount == null || currency == null || !cur) return null; return (amount * cur.buy) }; currency.formatForView = function(amount, currency) { let cur = ticker[currency]; if (!cur) return null; return cur.symbol + amount.toFixed(cur.decimalPlaces); }; currency.getList = function() { var array = []; console.log(ticker); angular.forEach(ticker, function(value) { array.push({ code: value.code, name: value.name }); }); return array; }; currency.getTicker(); return currency; } function userFactory() { const user = { settings: { currency: "USD" } } user.change = function(to) { user.settings.currency = to; } return user; } currencyFilter.$inject = ["currency", "User"]; function currencyFilter(currencyFactory, User) { return function(amount, currency = User.settings.currency, display = true) { let converted = currencyFactory.convertFromUsd(amount, currency); return display ? currencyFactory.formatForView(converted, currency) : converted; } } function BaseCtrl($scope, User, currency) { $scope.currencies = currency.getList() $scope.current = User.settings.currency; } 
 <!DOCTYPE html> <html ng-app="exampleApp"> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script> <link rel="stylesheet" href="style.css" /> <script src="script.js"></script> </head> <body ng-controller="BaseCtrl"> <h1>{{ 100.75 | currency:current }}</h1> <select ng-model="current" ng-options="currency.code as currency.name for currency in currencies"> </select> </body> </html> 

Update: the filter help indicates that the functions used as filters should be clean , which means they should not have a state, and they should be idempotent, i.e. one input parameter corresponds to one result. This allows the angular to perform a small optimization causing the filter only when the input parameters have changed.

But since sometimes it is necessary for the filter to store its state, an $stateful flag is added to the angular point, indicating that the filter has a state and needs to be calculated at each iteration of the digest loop.

Thus, using this flag, the example will look like this:

 "use strict"; angular .module("exampleApp", []) .factory('currency', currencyFactory) .factory("User", userFactory) .filter("currency", currencyFilter) .controller("BaseCtrl", BaseCtrl); function currencyFactory() { var ticker; const currency = {}; currency.getTicker = function() { ticker = { "USD": { "buy": 1, "sell": 1, "symbol": "$", "decimalPlaces": 3, "name": "US Dollar", "code": "USD" }, "EUR": { "buy": 0.885, "sell": 0.885, "symbol": "€", "decimalPlaces": 3, "name": "Euro", "code": "EUR" } } // FakeRest return ticker; } currency.convertToUsd = function(amount, currency) { if (amount == 0) return 0; let cur = ticker[currency]; if (amount == null || currency == null || !cur) return null; return (amount / cur.sell) }; currency.convertFromUsd = function(amount, currency) { if (amount == 0) return 0; let cur = ticker[currency]; if (amount == null || currency == null || !cur) return null; return (amount * cur.buy) }; currency.formatForView = function(amount, currency) { let cur = ticker[currency]; if (!cur) return null; return cur.symbol + amount.toFixed(cur.decimalPlaces); }; currency.getList = function() { var array = []; console.log(ticker); angular.forEach(ticker, function(value) { array.push({ code: value.code, name: value.name }); }); return array; }; currency.getTicker(); return currency; } function userFactory() { const user = { settings: { currency: "USD" } } user.change = function(to) { user.settings.currency = to; } return user; } currencyFilter.$inject = ["currency", "User"]; function currencyFilter(currencyFactory, User) { function filter(amount, display = true) { console.log('call filter', User.settings.currency); let converted = currencyFactory.convertFromUsd(amount, User.settings.currency); return display ? currencyFactory.formatForView(converted, User.settings.currency) : converted; } filter.$stateful = true; return filter; } function BaseCtrl($scope, User, currency) { $scope.currencies = currency.getList(); $scope.current = User.settings; } 
 <!DOCTYPE html> <html ng-app="exampleApp"> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script> <link rel="stylesheet" href="style.css" /> <script src="script.js"></script> </head> <body ng-controller="BaseCtrl"> <h1>{{ 100.75 | currency }}</h1> <select ng-model="current.currency" ng-options="currency.code as currency.name for currency in currencies"> </select> </body> </html> 

  • Thank! This is what I wanted. - blits
  • @Andrey, pay attention that when using this function the filter will be called at least twice at each iteration of the digest-cycle, therefore it is better not to use complex calculations there. - Grundy