2013-09-27 55 views
21

Tôi đang gặp sự cố khi cố gắng khởi tạo bộ lọc với dữ liệu không đồng bộ.AngularJS: Khởi tạo bộ lọc không đồng bộ

Bộ lọc rất đơn giản, cần dịch đường dẫn để đặt tên, nhưng để làm như vậy, nó cần một mảng tương ứng mà tôi cần tìm nạp từ máy chủ.

tôi có thể làm mọi thứ trong định nghĩa bộ lọc, trước khi trở về chức năng, nhưng khía cạnh không đồng bộ ngăn chặn mà

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

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

Sử dụng một lời hứa có thể sống được nhưng tôi không có bất kỳ sự hiểu biết rõ ràng về tải cách góc cạnh bộ lọc. Điều này post giải thích làm thế nào để đạt được ma thuật như vậy với các dịch vụ, nhưng nó có thể làm tương tự cho các bộ lọc?

Và nếu có ai có ý tưởng tốt hơn về cách dịch các đường dẫn đó, tôi là tất cả các tai.

EDIT:

Tôi đã thử với approch lời hứa, nhưng có điều gì đó không đúng, và tôi không thấy gì:

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

Tôi không thực sự familliar với những lời hứa, là nó đúng cách để sử dụng chúng?

+0

Còn bây giờ các bộ lọc fetchs dữ liệu correspondance, và đặt nó trong một var bên trong bộ lọc. Cuộc chiến này sau đó được sử dụng để dịch những thứ. Điều này không hoàn hảo, nếu phản hồi của máy chủ mất nhiều thời gian, bộ lọc sẽ không có dữ liệu sửa đổi, trong trường hợp đó nó không dịch. – Davounet

+0

Bạn có thể trả lời lời hứa về chức năng: http://docs.angularjs.org/api/ng.$q –

Trả lời

38

Dưới đây là một ví dụ:

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 Đây là một cuộc biểu tình sử dụng timeouts thay vì dịch vụ.


EDIT: Theo nhận xét của sgimeno, bạn phải cẩn thận khi không gọi dịch vụ nhiều lần. Xem các thay đổi trong số serviceInvoked trong mã ở trên và các câu đố. Xem fiddle cũng chia hai với 1.2.1 góc và một nút để thay đổi giá trị và kích hoạt tiêu hóa chu kỳ: forked fiddle


EDIT 2: Theo nhận xét của Miha Eržen, giải pháp này không có công việc logner cho góc 1.3. Tuy nhiên, giải pháp này gần như tầm thường, sử dụng cờ lọc $stateful, được ghi thành here trong "Bộ lọc trạng thái" và forked fiddle cần thiết.

Lưu ý rằng giải pháp này sẽ làm tổn thương hiệu suất, vì bộ lọc được gọi là mỗi chu kỳ phân hủy. Sự suy thoái hiệu suất có thể không đáng kể hay không, tùy thuộc vào trường hợp cụ thể.

+0

hoàn hảo! ước gì tôi có thể tăng gấp ba lần bầu cử –

+0

Chỉ cần cảm ơn bạn. Câu trả lời tuyệt vời –

+0

+1 Thực sự thành ngữ. – sgimeno

18

Hãy bắt đầu với sự hiểu biết tại sao mã gốc không hoạt động. Tôi đã đơn giản hóa câu hỏi ban đầu một chút để làm cho nó rõ ràng hơn:

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

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

} 

Về cơ bản, bộ lọc gọi hàm async trả về lời hứa, sau đó trả về giá trị của nó. Bộ lọc trong góc dự kiến ​​bạn trả về một giá trị có thể in dễ dàng, ví dụ: chuỗi hoặc số. Tuy nhiên, trong trường hợp này, mặc dù nó có vẻ như chúng ta đang trả lại response của getCorresp, chúng tôi đang thực sự trả lại một lời hứa mới - Giá trị trả về của bất kỳ then() hoặc catch() chức năng là một lời hứa .

Góc đang cố gắng chuyển đổi đối tượng lời hứa thành chuỗi thông qua truyền, không nhận được gì hợp lý để trả lại và hiển thị chuỗi rỗng.


Vì vậy, những gì chúng ta cần làm là, trả lại một tạm thời chuỗi giá trị và thay đổi nó asynchroniously, như vậy:

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

Mỗi lần Angular cố thực thi bộ lọc, nó sẽ kiểm tra xem bản dịch đã được tải chưa và nếu không, nó sẽ trả về giá trị "Đang tải ...". Chúng tôi cũng sử dụng giá trị isWaiting để ngăn việc gọi dịch vụ nhiều lần.

Ví dụ ở trên hoạt động tốt cho Angular 1.2, tuy nhiên, trong số các thay đổi trong Góc 1.3, có cải thiện hiệu suất làm thay đổi hành vi của bộ lọc. Trước đây, chức năng lọc được gọi là mỗi chu kỳ tiêu hóa. Tuy nhiên, kể từ 1.3, nó chỉ gọi bộ lọc nếu giá trị được thay đổi, trong mẫu cuối cùng của chúng tôi, nó sẽ không bao giờ gọi lại bộ lọc - 'WelcomeTo' sẽ không bao giờ thay đổi.

May mắn việc sửa chữa rất đơn giản, bạn sẽ chỉ cần thêm vào các bộ lọc như sau:

JSFiddle

myFilter.$stateful = true; 

Cuối cùng, trong khi đối phó với vấn đề này, tôi có vấn đề khác - Tôi cần sử dụng bộ lọc để nhận các giá trị không đồng bộ mà có thể thay đổi - Cụ thể, tôi cần tìm nạp bản dịch cho một ngôn ngữ, nhưng sau khi người dùng đã thay đổi ngôn ngữ, tôi cần tìm nạp bộ ngôn ngữ mới. Làm điều đó, tỏ ra phức tạp hơn một chút, mặc dù khái niệm là như nhau. Đây là mã rằng:

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

Có một vấn đề lớn với sự chấp thuận của bạn là nếu bạn khởi động bộ lọc 50 lần trong một ng-lặp lại nó sẽ gọi dịch vụ 50 lần thay vì gọi nó một lần và sau đó phân tích thông tin thông qua xuống đến bộ lọc –

+1

@SimonPertersen Đó không phải là điều nên xảy ra. Trong khi dịch vụ đang tìm nạp bản dịch 'isWaiting' sẽ là' true' vì vậy tất cả các bộ lọc sau sẽ chỉ trả về 'Đang tải ...' – VitalyB

+0

@VitalyB, theo https://code.angularjs.org/1.3.7/docs/guide/filter # stateful-filters, điều này không được khuyến khích. Có công việc nào để đạt được điều này không? –

Các vấn đề liên quan