AngularJS:异步初始化过滤器

9 浏览
0 Comments

AngularJS:异步初始化过滤器

我正在尝试使用异步数据初始化一个过滤器。

这个过滤器非常简单,它需要将路径转换为名称,但是为了做到这一点,它需要一个相应的数组,我需要从服务器获取。

我可以在过滤器定义中做一些事情,在返回函数之前,但是异步方面阻碍了我。

angular.module('angularApp').
  filter('pathToName', function(Service){
    // Do some things here
    return function(input){
      return input+'!'
    }
  }

使用一个promise可能是可行的,但我并不清楚angular如何加载过滤器。

这个post解释了如何通过服务实现这种神奇,但是过滤器也可以这样做吗?

如果有人有更好的想法来翻译这些路径,我全听取建议。

编辑:

我试着使用promise方法,但是有些不对劲,我不知道是什么问题:

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;
      }
    )
  };
});

我不太熟悉promise,使用它是正确的方法吗?

admin 更改状态以发布 2023年5月24日
0
0 Comments

首先,让我们来理解原始代码为什么不起作用。我简化了原始问题,以使其更加清晰:

angular.module('angularApp').filter('pathToName', function(Service) {
    return function(input) {
        return Service.getCorresp().then(function(response) {
            return response;
        });
    });
}

基本上,该过滤器调用了一个返回承诺的异步函数,然后返回其值。在 Angular 中,过滤器期望您返回一个可以轻松打印的值,例如字符串或数字。但是,在这种情况下,即使看起来我们正在返回 getCorrespresponse,我们实际上正在返回一个新承诺 - 任何 then()catch() 函数的返回值都是 承诺

Angular 试图通过转换将承诺对象转换为字符串,却没有得到有意义的返回值,因此显示为空字符串。


因此,我们需要返回一个临时的 字符串 值,并以异步方式进行更改,如下所示:

JSFiddle

HTML:

    {{'WelcomeTo' | translate}}
    {{'GoodBye' | translate}}

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 中的更改中,有一项性能改进改变了过滤器的行为。以前的过滤器函数在每个 digest 周期中都会被调用。但自 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;
});

0
0 Comments

这里有一个示例:

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);
    }
});

这个fiddle演示了使用超时而不是服务的方法。


编辑:根据sgimeno的评论,必须特别小心,不要调用超过一次的服务。请查看上面代码的serviceCalled更改以及fiddles。还可以使用Angular 1.2.1的forked fiddle并添加按键以更改值并触发digest cycles:forked fiddle


编辑2:根据Miha Eržen的评论,这个解决方案对于Angular 1.3不再可行。然而,解决方案几乎是微不足道的,只需要使用$stateful过滤器标志,在这里的“Stateful filters”下有文档,以及必要的forked fiddle

请注意,这个解决方案会影响性能,因为每个digest cycle都会调用过滤器。性能下降可能可以忽略或不可忽略,具体情况取决于特定的情况。

0