#javascript #ajax #angularjs #asynchronous #filter
#javascript #ajax #angularjs #асинхронный #Фильтр
Вопрос:
Недавно я работал над проектом AngularJS и столкнулся с интересной проблемой при попытке создать фильтр, который использует данные, загруженные через AJAX-запрос.
Сначала о проблеме:
Фильтр AngularJS — это синхронный фрагмент кода (функция), который возвращает строку, которая вставляется в ваш DOM. И в большинстве случаев это работает отлично, например, следующий фильтр, который заглавными буквами заполняет первую букву:
angular.module('myApp.filters', []).filter('capitilize', function() {
return function (word) {
return word.charAt(0).toUpperCase() word.substr(1);
}
});
И это отлично работает. Теперь вопрос в том, что, если я не смогу сразу вернуть желаемый результат? Допустим, мне нужно загрузить некоторые данные с помощью AJAX-запроса, чтобы получить желаемый результат. Если я сделаю запрос AJAX, мой оператор return вернет пустой результат до того, как я получу свои данные. Итак, на самом деле вопрос в том, как мне уведомить filter, чтобы он обновлял себя при загрузке моих данных?
Решение:
Оказалось, что решение было прямо передо мной, но мне потребовалось некоторое время, чтобы понять, как происходит волшебство. Допустим, мне нужен фильтр, который извлекает биографию исполнителя на основе его имени (да, немного сумасшедший пример, но он доказывает суть):
angular.module('myApp.filters', []).filter('biography', function($q, $http) {
var pending = {};
return function (artist) {
if ( !(artist in pending) ) {
pending[artist] = null;
$http.get('http://developer.echonest.com/api/v4/artist/biographies?api_key=FILDTEOIK2HBORODVamp;name='
artist 'amp;format=jsonamp;results=1amp;start=0amp;license=cc-by-sa')
.then(function(response){
pending[artist] = response.data.response.biographies[0].text;
});
}
return pending[artist] || '';
}
});
Это работает, но как? Я сделал запрос get, получил свой результат, но как это заставляет filter обновляться самостоятельно. Ключевым моментом здесь является $ q от angular (обещанная / отложенная реализация, вдохновленная Q. Криса Коваля)
из документации angular:
$ q интегрирован с $rootScope.Механизм наблюдения за моделью области видимости в angular, который означает более быстрое распространение разрешения или отклонения в ваших моделях и позволяет избежать ненужных перерисовок браузера, которые привели бы к мерцанию пользовательского интерфейса.
Это означает, что всякий раз, когда обещание разрешается, оно вызывает обновление, фактически вот код из angular:
function done(status, response, headersString, statusText) {
if (cache) {
if (isSuccess(status)) {
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
} else {
// remove promise from the cache
cache.remove(url);
}
}
resolvePromise(response, status, headersString, statusText);
if (!$rootScope.$$phase) $rootScope.$apply();
}
Надеюсь, это было полезно для вас. Вот пример, имейте в виду, что в примере выполняются междоменные вызовы ajax, вам нужно будет отключить междоменную политику вашего браузера:
ВАЖНО: Имейте в виду, чтобы не перегружать фильтр одним и тем же ajax-запросом, иначе вы можете столкнуться с этим:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
Комментарии:
1. Так это вопрос с самостоятельным ответом? Похоже на один. Если вы хотите это сделать, вы должны опубликовать вопросную часть как вопрос, а ответ — как фактический ответ. Включение всего этого в вопрос немного сбивает с толку.
Ответ №1:
спасибо за этот пост, по крайней мере, я знал, что я не совсем неправ. У меня очень похожий сценарий:
1) простой фильтр для перевода
module.filter('translate', ['Localization', function (localization) {
var translateFilter = function (key) {
return localization.get(key) || "[" key "]";
};
translateFilter.$stateful = true;
return translateFilter;
}]);
2) служба локализации, которая хранит словарь JS. Это обновляется через XHR. Это означает, что существует несколько возможных состояний словаря:
- нет локализации перед инициализацией => пусто
- локализация по умолчанию после инициализации => непустой
- пользовательская локализация после загрузки по умолчанию => непустой
Моя проблема заключалась в том, что результат фильтра не был обновлен (повторно отображен) после завершения вызова XHR, даже несмотря на то, что был выполнен дайджест. Мне пришлось добавить
translateFilter.$stateful = true;
чтобы заставить его работать. Даже изменение зависимости фильтра с service
на value
не помогло. Кто-то может посчитать это полезным, или лучше событие, скажите мне, что я делал не так 🙂