Требуется ли для вызова метода пользовательских фильтров AngularDart идемпотентность?
Основным примером работы с учебником Angular Dart является приложение "Книга рецептов". В конце главы 5, посвященной фильтрам и услугам, предлагается попытаться " создать [пользовательский] фильтр, который умножит все количества [каждого ингредиента в списке] в рецептах ", что позволит " пользователю удвоить, утроить или рецепт в четыре раза ". Например, ингредиент "1/2 стакана муки" станет "1 стаканом муки", если его удвоить.
Я написал такой специальный фильтр: он принимает список Ingredient
с (состоящий из quantity
и description
) и возвращает новый список новых Ingredient
s (с увеличенными количествами), но я получаю следующую ошибку:
5 $digest() iterations reached. Aborting!
Мой вопрос: каково обязательное и / или разрешенное поведение пользовательского фильтра AngularDart call()
метод? Например, очевидно, что разрешено удалять (то есть фильтровать) элементы из своего входного списка, но может ли он также добавлять новые или заменять элементы? Документация Dart angular.core NgFilter просто говорит, что "фильтр - это класс с методом вызова". Я не нашел больше деталей.
Экстраполируя из ответа на этот пост AngularJS, казалось бы, что повторные вызовы call()
должен (в конце концов?) дать "тот же результат". Если это так, это будет разумным ограничением.
Дать "тот же результат" может означать, что call()
должен быть идемпотентом, но в случае дартса такой идемпотент должен быть относительно ==
(эквивалентность объекта) нет identical()
(идентичность объекта), ИМХО. Я провел несколько тестов, используя следующий небольшой пример, чтобы проиллюстрировать проблемы:
- main.dart
import 'package:angular/angular.dart';
class A { }
@NgFilter(name:'myFilter') class MutatingCustomFilter {
final A _a = new A();
call(List list) => new List.from(list)..add(_a); // runs ok.
// call(List list) => new List.from(list)..add(new A()); // gives error
}
class MyAppModule extends Module {
MyAppModule() { type(MutatingCustomFilter); }
}
main() => ngBootstrap(module: new MyAppModule());
- index.html выдержка
<ul>
<li ng-repeat="x in [1,2,3] | myFilter">{{x}}</li>
</ul>
Если я изменю тело class A
быть
@override bool operator==(other) => true;
@override int get hashCode => 1;
что делает все случаи A
считается ==
затем вторая реализация call()
в main.dart (тот, с add(new A())
) все равно выдает ошибку (хотя и другую).
Я вижу, как решить учебное упражнение без использования специального фильтра, но я стараюсь не разочаровываться в поиске фильтра, который будет работать в соответствии с требованиями. Я новичок в Angular и решил присоединиться к AngularDart, поэтому любая помощь в объяснении эффектов различных вкусов call()
или в поиске документации для ожидаемого поведения call()
(или дайте мне знать, если вы думаете, что такой пользовательский фильтр просто не может быть написан!) будет принята с благодарностью.
1 ответ
Слишком много итераций
Когда angular обнаруживает изменение в модели, он выполняет функцию реакции. Функция реакции может дополнительно изменить модель. Это оставило бы модель в несовместимом состоянии. По этой причине мы повторно запускаем обнаружение изменений, что может в дальнейшем создать больше изменений. По этой причине мы продолжаем повторять изменения до тех пор, пока модель не стабилизируется. Но сколько раз мы должны повторно запускать обнаружение изменений, прежде чем сдаваться? По умолчанию это 5 раз. Если модель не стабилизируется после 5 итераций, мы сдаемся. Это то, что происходит в вашем случае.
Обнаружение изменений
Когда объект изменился? можно использовать identical
или же ==
(Равно). Хорошие аргументы могут быть сделаны для каждого, но мы решили использовать identical
потому что это быстро и последовательно. С помощью ==
(равно) сложно, и это негативно повлияет на алгоритм обнаружения изменений.
Фильтры и массивы
Когда фильтр, который работает с массивом, выполняет его, у него нет другого выбора, кроме как создать новый экземпляр массива. Это ломается идентично, но, к счастью, оно подается в ng-repeat
который использует свой собственный алгоритм для обнаружения содержимого массива, а не для обнаружения массива. В то время как массив не должен быть идентичным между запусками, его содержимое должно быть. Иначе ng-repeat
не могу отличить разницу между вставками и изменениями, которые нужны для правильной анимации.
Ваш код
Проблема с вашим фильтром заключается в том, что он создает новый экземпляр на каждой итерации цикла дайджеста. Эти новые случаи препятствуют стабилизации модели и, следовательно, ошибке. (Есть планы решить эту проблему, но это займет несколько недель, прежде чем мы туда доберемся.)
Решение
Ваши решения пытаются создать фильтр, который использует весь массив, а затем пытается создать новый массив для ng-repeat
, Другим (предпочтительным) решением было бы оставить ng-repeat
итерации, как есть, и вместо этого поместите фильтр на привязку, которая создает кол-во и примените его там.
<span>{{recipe.qty | myFilter:multiply}}</span>