Cabadath
Cabadath

Reputation: 977

AngularJS refresh interpolated value

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.

Upvotes: 2

Views: 1407

Answers (2)

AWolf
AWolf

Reputation: 8990

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>

Upvotes: 4

Kevin Dre&#223;ler
Kevin Dre&#223;ler

Reputation: 447

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.

Upvotes: 0

Related Questions