2

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.

0

2 Answers 2

4

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>

Sign up to request clarification or add additional context in comments.

1 Comment

That's what I was looking for. Thank you for the explanation and doc links.
0

I was interested in this as well, so I studied the angular-translate code.

In their code, in particular in src/service/translate.js and src/filter/translate.js, you will see they keep track of interpolation-ids and update the interpolation-object after $translate.setLanguage(). It's really the only difference between your code and theirs.

My guess here is that angular will keep track of the changes to interpolation objects inbetween digest cycles and re-run changed interpolations although their context hasn't changed, but i don't happen to know the angular code base so well that i can prove this. It just would make sense to me as I dont see any 'trickery' other than that going on in angular-translate's code.

2 Comments

I did not really understand what you meant by "update the interpolation-object" (I could not find the $translate.setLanguage() function, I guess we might have different version?). What I found though was in src/filter/translate.js, they use translateFilter.$stateful = true; which apparently does the trick - need to check with documentation what that actually is/does.
I was referring to this function: github.com/angular-translate/angular-translate/blob/master/src/… Anyway, good to know $stateful now!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.