AngularJS:异步初始化过滤器
AngularJS : Asynchronously initialize filter
我在尝试使用异步数据初始化过滤器时遇到问题。
过滤器非常简单,它需要将路径转换为名称,但要做到这一点,它需要一个对应数组,我需要从服务器获取。
在返回函数之前,我可以在过滤器定义中做一些事情,但是异步方面阻止了这一点
angular.module('angularApp').
filter('pathToName', function(Service){
// Do some things here
return function(input){
return input+'!'
}
}
使用承诺可能是可行的,但我对角度载荷如何过滤没有任何清晰的理解。这篇文章解释了如何通过服务实现这种魔力,但是是否可以对过滤器做同样的事情?
如果有人对如何翻译这些路径有更好的想法,我会全力以赴。
编辑:
我尝试了承诺approch,但有些不对劲,我看不到什么:
angular.module('angularApp').filter('pathToName', function($q, Service){
var deferred = $q.defer();
var promise = deferred.promise;
Service.getCorresp().then(function(success){
deferred.resolve(success.data);
}, function(error){
deferred.reject();
});
return function(input){
return promise.then(
function(corresp){
if(corresp.hasOwnProperty(input))
return corresp[input];
else
return input;
}
)
};
});
我对承诺不太熟悉,这是使用它们的正确方法吗?
一个示例:
app.filter("testf", function($timeout) {
var data = null, // DATA RECEIVED ASYNCHRONOUSLY AND CACHED HERE
serviceInvoked = false;
function realFilter(value) { // REAL FILTER LOGIC
return ...;
}
return function(value) { // FILTER WRAPPER TO COPE WITH ASYNCHRONICITY
if( data === null ) {
if( !serviceInvoked ) {
serviceInvoked = true;
// CALL THE SERVICE THAT FETCHES THE DATA HERE
callService.then(function(result) {
data = result;
});
}
return "-"; // PLACEHOLDER WHILE LOADING, COULD BE EMPTY
}
else return realFilter(value);
}
});
此小提琴是使用超时而不是服务的演示。
编辑:根据sgimeno的评论,必须格外小心,不要多次调用该服务。请参阅上述代码和小提琴中的serviceInvoked
更改。另请参阅带有 Angular 1.2.1 的分叉小提琴和一个用于更改值和触发摘要循环的按钮:分叉小提琴
编辑2:根据Miha Eržen的评论,此解决方案对Angular 1.3不起作用。不过,解决方案几乎是微不足道的,使用 $stateful
过滤器标志(此处记录在"有状态过滤器"下(和必要的分叉小提琴。
请注意,此解决方案会影响性能,因为每个摘要周期都调用筛选器。性能下降可以忽略不计,也可以不计,具体取决于具体情况。
让我们从理解为什么原始代码不起作用开始。我稍微简化了原始问题,使其更清晰:
angular.module('angularApp').filter('pathToName', function(Service) {
return function(input) {
return Service.getCorresp().then(function(response) {
return response;
});
});
}
基本上,过滤器调用一个异步函数,该函数返回承诺,然后返回其值。角度过滤器希望您返回一个可以轻松打印的值,例如字符串或数字。但是,在这种情况下,即使看起来我们正在返回 getCorresp
的response
,我们实际上也在返回一个新的 promise - 任何 then()
或 catch()
函数的返回值都是一个 promise。
Angular 试图通过强制转换将 promise 对象转换为字符串,但没有得到任何合理的回报并显示一个空字符串。
因此,我们需要做的是,返回一个临时字符串值并异步更改它,如下所示:
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;
});
每次 Angular 尝试执行过滤器时,它都会检查翻译是否已被获取,如果没有,它将返回"正在加载..."价值。我们还使用 isWaiting
值来防止多次调用服务。
上面的示例适用于 Angular 1.2,但是,在 Angular 1.3 的变化中,有一个性能改进改变了过滤器的行为。以前,每个摘要周期都调用过滤器函数。但是,从 1.3 开始,它仅在值更改时才调用过滤器,在我们的上一个示例中,它永远不会再次调用过滤器 - 'WelcomeTo'
永远不会更改。
幸运的是,修复非常简单,您只需要将以下内容添加到过滤器中:
JSFiddle
myFilter.$stateful = true;
最后,在处理这个问题时,我遇到了另一个问题 - 我需要使用过滤器来获取可以更改的异步值 - 具体来说,我需要获取一种语言的翻译,但是一旦用户更改了语言,我需要获取新的语言集。这样做,被证明有点棘手,尽管概念是相同的。这是代码:
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) {
// Sample object: {"en": {"WelcomeTo": {translation: "Welcome!!", processing: false } } }
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;
});
- 使用javascript函数在页面初始化后加载jquery
- 未捕获错误:无法在初始化之前调用方法;
- 如何在剑道网格初始化后设置pageSizes
- React redux初始化功能,无论状态变化如何
- 为什么可以't Protractor在自动初始化的Angular网站上查找Angular
- 尝试初始化()Spine's控制器和故障
- Angular.js延迟控制器初始化
- 对插件初始化后动态加载的元素进行样式设置
- Angular JS控制器初始化错误
- 在XMLHttpRequest之后重新初始化jQuery
- ember-js组件初始化不同的函数
- Youtube JavaScript API不是't正在初始化
- 函数无法识别初始化的变量
- datepicker引导程序再次初始化
- jquery数据表在初始化时设置宽度
- 如何使用app和secret进行安全的解析初始化
- 从javascript中再次初始化c变量
- ng重复中的ng模型-初始化唯一作用域属性
- AngularJS:异步初始化过滤器
- DHTMLX网格过滤器复位后的初始化延迟