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>
ng-optionsinstead ofng-repeat- Grundydirectiveyou wanted tofilter- Grundy