To understand why the original code is not functioning, let's break it down into simpler terms:
angular.module('angularApp').filter('pathToName', function(Service) {
return function(input) {
return Service.getCorresp().then(function(response) {
return response;
});
});
}
The problem lies in the fact that the filter is calling an asynchronous function that returns a promise and then attempts to return its value. However, filters in Angular are designed to handle synchronous data types like strings or numbers for output. In this case, even though it appears that we are returning the response
of getCorresp
, we are actually returning a new promise - as the return value of any then()
or catch()
function is a promise.
Angular struggles to convert a promise object to a string, resulting in an empty display.
To resolve this issue, we need to modify the approach by initially returning a temporary string value and updating it asynchronously, as shown below:
JSFiddle
HTML:
<div ng-app="app" ng-controller="TestCtrl">
<div>{{'WelcomeTo' | translate}}</div>
<div>{{'GoodBye' | translate}}</div>
</div>
Javascript:
app.filter("translate", function($timeout, translationService) {
var isWaiting = false;
var translations = null;
function myFilter(input) {
var translationValue = "Loading...";
if(translations)
{
translationValue = translations[input];
} else {
if(isWaiting === false) {
isWaiting = true;
translationService.getTranslation(input).then(function(translationData) {
console.log("GetTranslation done");
translations = translationData;
isWaiting = false;
});
}
}
return translationValue;
};
return myFilter;
});
Whenever the filter is triggered, Angular checks if the translations have been fetched already; otherwise, it displays "Loading...". Additionally, the isWaiting
flag prevents redundant service calls.
In Angular 1.3, there was a performance enhancement altering how filters behave. Previously, the filter function would execute every digest cycle. Since 1.3, it triggers only when the value changes, potentially causing static values like 'WelcomeTo'
to remain unchanged indefinitely.
To address this, simply add the following line to the filter:
JSFiddle
myFilter.$stateful = true;
During the troubleshooting process, I encountered another challenge wherein I needed a filter to retrieve asynchronous values that might change dynamically. Specifically, I aimed to fetch translations for a language that could be updated based on user interactions. Here's the revised code snippet:
JSFiddle
var app = angular.module("app",[]);
debugger;
app.controller("TestCtrl", function($scope, translationService) {
$scope.changeLanguage = function() {
translationService.currentLanguage = "ru";
}
});
app.service("translationService", function($timeout) {
var self = this;
var translations = {"en": {"WelcomeTo": "Welcome!!", "GoodBye": "BYE"},
"ru": {"WelcomeTo": "POZHALUSTA!!", "GoodBye": "DOSVIDANYA"} };
this.currentLanguage = "en";
this.getTranslation = function(placeholder) {
return $timeout(function() {
return translations[self.currentLanguage][placeholder];
}, 2000);
}
})
app.filter("translate", function($timeout, translationService) {
var translated = {};
var isWaiting = false;
myFilter.$stateful = true;
function myFilter(input) {
if(!translated[translationService.currentLanguage]) {
translated[translationService.currentLanguage] = {}
}
var currentLanguageData = translated[translationService.currentLanguage];
if(!currentLanguageData[input]) {
currentLanguageData[input] = { translation: "", processing: false };
}
var translationData = currentLanguageData[input];
if(!translationData.translation && translationData.processing === false)
{
translationData.processing = true;
translationService.getTranslation(input).then(function(translation) {
console.log("GetTranslation done");
translationData.translation = translation;
translationData.processing = false;
});
}
var translation = translationData.translation;
console.log("Translation for language: '" + translationService.currentLanguage + "'. translation = " + translation);
return translation;
};
return myFilter;
});