I'm trying to figure out, how to update interpolated values in an Angular template - if the value itself does not change.
I have statements like this (e.g. a translation):
<div>{{ 'SOME_TAG' | lang }}</div>
This starts a filter
app.filter('lang', function(Locale) {
return function(val) {
return Locale.translate(val);
}
});
Locale is a service that has a field lang, depending on which the values are translated into different languages. When I change the value of this field, the value of the translation should change as well.
Something like
<select ng-model="lang" ng-change="changeLang()">
<option value="de">Deutsch</option>
<option value="en">English</option>
</select>
...
$scope.changeLang = function() {
Locale.changeLanguage($scope.lang);
};
In older AngularJS versions (e.g. 1.1.5, not sure how far) this did the trick just fine. If I update the Locale.lang value and a $digest() cycle runs, the interpolated template values are updated. In the newest version (1.4.6 but also 1.3.xx) this does not work anymore. I assume this is part of some optimization - the value ('SOME_TAG') has not changed, so why rerun the interpolation.
I've seen this http://angular-translate.github.io/ where it works fine. Is there a "trick" to have those values update?
Thank you.
In the angular docs about filters there is something called stateful
. It was added to improve performance of filters.
There's a very good blog post about stateful filters that's describing it in detail.
In short 'SOME_TAG'
is a string and never changes and the $watch
that the filter is adding would never be called. But with $stateful
it will also check if there is a change in the injected dependency of your filter.
So you would need $stateful
or you could pass your language scope variable to the service to have it working.
I think it's better to avoid $stateful
filters (as recommended in the docs) because they're running more often then with-out it. So passing the language scope will be better here.
Please have a look at the demo below or in this fiddle.
angular.module('demoApp', [])
.factory('Locale', Locale)
.filter('langStateful', LangFilterState)
.filter('lang', LangFilter)
.controller('MainController', MainController);
function MainController($scope, $timeout, Locale) {
$scope.changeLang = function() {
Locale.changeLanguage($scope.lang);
};
}
function LangFilter(Locale) {
function LangFilter(val, lang) {
return Locale.translateLang(val, lang);
}
return LangFilter;
}
function LangFilterState(Locale) {
function LangFilter(val) {
return Locale.translate(val);
}
LangFilter.$stateful = true;
return LangFilter;
}
function Locale() {
var localeFactory = {
language: 'de',
changeLanguage: changeLanguage,
translate: translate,
translateLang: translateLang,
tags: {
'title': {
de: 'Titel',
en: 'title'
},
'help': {
de: 'Hilfe',
en: 'help'
}
}
};
return localeFactory;
function changeLanguage(lang) {
this.language = lang;
}
function translate(tag) {
return this.tags[tag][this.language];
}
function translateLang(tag, lang) {
return this.tags[tag][lang];
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>
<div ng-app="demoApp" ng-controller="MainController">
<select ng-model="lang" ng-change="changeLang()" ng-init="lang='de'">
<option value="de">Deutsch</option>
<option value="en">English</option>
</select>
{{'help' | langStateful}}
{{'title' | lang: lang}}
</div>