2013-09-27 124 views
21

我無法嘗試使用異步數據初始化過濾器。AngularJS:異步初始化過濾器

該過濾器非常簡單,它需要將路徑轉換爲名稱,但爲此需要一個對應數組,我需要從服務器獲取數據。

我可以做的事情在過濾器定義,返回功能之前,但異步方面防止

angular.module('angularApp'). 
    filter('pathToName', function(Service){ 
    // Do some things here 

    return function(input){ 
     return input+'!' 
    } 
    } 

使用的承諾可能是可行的,但我並沒有對如何角加載任何明確的認識過濾器。 這個post解釋瞭如何通過服務實現這樣的魔術,但是否可以爲過濾器做同樣的事情?

如果任何人對如何翻譯這些路徑有更好的想法,我全都聽過。

編輯:

我試圖與承諾計算策略,但東西是不正確的,我看不出有什麼:

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

我不是真正的承諾familliar,是它的正確的方式來使用它們?

+0

對於現在的過濾器去抓取的對應關係數據,並將其放入過濾器內的變種。這場戰爭然後被用來翻譯事情。 事情是不完美的,如果服務器響應花費很多時間,過濾器將不會有對應的數據,在這種情況下,它不會翻譯。 – Davounet

+0

您可以返回該功能的承諾:http://docs.angularjs.org/api/ng.$q –

回答

38

下面是一個例子:

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的評論,必須格外小心不要多次調用服務。看到serviceInvoked上面的代碼和小提琴的變化。見有角1.2.1和按鈕也分叉小提琴更改值並觸發消化週期:forked fiddle


編輯2:按照米哈Eržen的評論,這個解決方案確實對角1.3沒有logner工作。該解決方案几乎微不足道,使用$stateful過濾器標誌,在「狀態過濾器」下記錄爲here,並且必需的forked fiddle

請注意該解決方案會損害性能,因爲過濾器被稱爲每個消化週期。性能下降可能可以忽略不計,具體情況取決於具體情況。

+0

完美!希望我可以三倍投票 –

+0

只是謝謝你。真棒回答 –

+0

+1真的很習慣。 – sgimeno

18

讓我們從理解原始代碼不起作用的開始。我已經簡化了原來的問題有點更清楚:

angular.module('angularApp').filter('pathToName', function(Service) { 

    return function(input) { 
     return Service.getCorresp().then(function(response) { 
      return response; 
     }); 
    }); 

} 

一般而言,過濾器調用返回的承諾異步函數,然後返回它的值。角度過濾器期望您返回可以輕鬆打印的值,例如字符串或數字。然而,在這種情況下,即使它看起來像我們返回的getCorrespresponse,我們實際上返回新承諾 - 任何then()catch()函數的返回值是一個承諾

Angular正試圖通過強制轉換承諾對象爲字符串,沒有得到任何合理的回報,並顯示一個空字符串。


因此,我們需要做的是,將臨時字符串值和asynchroniously改變它,就像這樣:

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嘗試執行過濾器,它會檢查翻譯是否已被提取,如果它們不是,它會返回「Loading ...」值。我們還使用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; 
}); 
+0

你的接近有一個大問題,就是如果你用ng-repeat初始化過濾器50次,它會調用服務50次,而不是調用一次,然後通過解析信息下到過濾器 –

+1

@SimonPertersen這不是應該發生的。當服務獲取翻譯'isWaiting'時會是'true',所以下面的所有過濾器將簡單地返回'Loading ...' – VitalyB

+0

@VitalyB,按照https://code.angularjs.org/1.3.7/docs/guide/filter#有狀態過濾器,這是非常不鼓勵的。是否有任何解決方法來實現這一目標? –