Это антипаттерн - использовать Angular's $watch в контроллере?

В моем бесконечном стремлении сделать вещи "правильным" угловым способом, я много читал о том, как заставить контролеров наблюдать за изменениями в моделях, используемых в угловых сервисах.

Некоторые сайты говорят, что использование $watch на контроллере категорически неправильно:

НЕ используйте $watch в контроллере. Это трудно проверить и совершенно не нужно почти в каждом случае. Используйте метод в области видимости, чтобы обновить значения, которые вместо этого менялись часы.

Другие, кажется, в порядке с этим, пока вы убираете за собой:

Сама функция $watch возвращает функцию, которая при вызове отменяет привязку $watch. Итак, когда $watch больше не нужен, мы просто вызываем функцию, возвращаемую $watch.

Есть SO вопросы и другие авторитетные сайты, которые, кажется, прямо говорят о том, что использование $watch в контроллере - отличный способ заметить изменения в модели с угловым обслуживанием.

Сайт https://github.com/angular/angular.js/wiki/Best-Practices, которому, я думаю, мы можем придать немного больший вес, прямо заявляет, что $scope.$ Watch должен заменить потребность в событиях. Однако для сложных SPA, которые обрабатывают более 100 моделей и конечных точек REST, выберите использование $watch, чтобы избежать событий с $broadcast/$emit может закончиться с большим количеством часов. С другой стороны, если мы не используем $watch, для нетривиальных приложений мы получаем множество событийных спагетти.

Это ситуация проигрыша? Это ложный выбор между событиями и часами? Я знаю, что вы можете использовать двустороннюю привязку для многих ситуаций, но иногда вам просто нужен способ прослушать изменения.

РЕДАКТИРОВАТЬ

Комментарий Илана Фрумера заставил меня переосмыслить то, что я спрашиваю, поэтому, возможно, вместо того, чтобы просто спрашивать, субъективно хорошо или плохо использовать $watch в контроллере, позвольте мне сформулировать вопросы следующим образом:

Какая реализация может создать узкое место в производительности в первую очередь? Контроллеры прослушивают события (которые должны были передаваться / передаваться) или настраивают $watchв контроллерах. Помните, масштабное приложение.

Какая реализация сначала создает головную боль при обслуживании: $watchили события? Возможно, существует связь (плотная / свободная) в любом случае... наблюдатели событий должны знать, что слушать, и $watch-на внешних ценностях (например, MyDataService.getAccountNumber()) обоим нужно знать о вещах, происходящих за пределами их границ.

** РЕДАКТИРОВАТЬ более года спустя **

Angular сильно изменился / улучшился с тех пор, как я задал этот вопрос, но я все еще получаю +1, поэтому я подумал, что упомяну, что при просмотре кода команды Angular я вижу шаблон, когда речь идет о наблюдателях в контроллерах (или директивы, где есть область, которая уничтожается):

$scope.$on('$destroy', $scope.$watch('scopeVariable', functionIWantToCall)); Для этого нужно взять то, что возвращает функция $watch - функцию, которая может быть вызвана для отмены регистрации наблюдателя, - и передать это обработчику событий, когда контроллер разрушен. Это автоматически очищает наблюдателя.

Независимо от того, являются ли часы в контроллерах запахом кода или нет, если вы их используете, я полагаю, что использование этой модели командой разработчиков должно послужить убедительной рекомендацией относительно того, как их использовать.

Спасибо!

3 ответа

Решение

Я использую оба, потому что, честно говоря, я рассматриваю их как разные инструменты для разных задач.

Я приведу пример из приложения, которое я создал. У меня был сложный WebSocket Service, который получал динамические модели данных с сервера веб-сокетов. Сама служба не заботится о том, как выглядит модель, но, конечно, контролер уверен, что делает.

Когда контроллер запускается, он устанавливает $watch на объекте данных службы, чтобы он знал, когда его конкретный объект данных прибыл (как ожидание Service.data.foo существовать. Как только эта модель появилась, она смогла привязаться к ней и создать двустороннюю привязку данных, часы устарели и были уничтожены.

С другой стороны, служба отвечала за трансляцию определенных событий, потому что иногда клиент получал буквальные команды от сервера. Например, сервер может запросить, чтобы клиент отправил некоторые метаданные, которые были сохранены в "$rootScope" по всему приложению. .on() был создан в $rootScope в течение module.run() шаг, чтобы прослушать эти команды с сервера, собрать необходимую информацию от других сервисов и перезвонить сервису WebSocket, чтобы отправить данные по запросу. Кроме того, если бы я сделал это с помощью $watch()Мне нужно было бы установить какую-то произвольную переменную для просмотра, например metadataRequests который мне нужно будет увеличивать каждый раз, когда я получаю запрос. broadcast достигает того же самого без необходимости жить в постоянной памяти, как наша переменная.

По сути, я использую $watch() когда есть конкретное значение, которое я хочу увидеть изменения (особенно если мне нужно знать значения до и после), и я использую события, если есть более высокоуровневые условия, которые были выполнены, что контроллеры должны знать около.

Что касается производительности, я не могу сказать вам, какой из них будет узким местом первым, но я чувствую, что если подумать об этом, то вы сможете использовать сильные стороны каждой функции, где они наиболее сильны. Например, если вы используете $on() вместо $watch() чтобы искать изменения в данных, у вас не будет доступа к значениям до и после изменения, что может ограничить то, что вы пытаетесь сделать.

Все, что является двусторонней привязкой данных, это $watch в какой бы области свойства вы ни дали ng-model, который имеет контроллер, который позволяет другие директивы, такие как input, а также form синхронизировать ng-model значение для отображения представления об изменении. Что обнаруживается при их регистрации событий в DOM.

По сути,ng-model"s $watch сравнивает значение в модели со значением, которое оно имеет внутри. Значение, которое оно имеет внутри, задается вспомогательными директивами (ввод, форма и т. Д.).

ИМХО Единственные "события", на которые вы должны реагировать в угловом приложении, создаются пользователем (то есть события DOM). Они решаются с помощью директив по DOM и ng-model связь с..моделью. Естественно, существует асинхронный режим, для которого $q для которого обратные вызовы вызывают $digest.

Что касается производительности, это говорит о том, что это действительно хорошо в угловых документах. Его запустить на каждом $digest, Так что сделай это быстро. Что каждый $digest? Angular пересекает все ваши активные области видимости. Каждая сфера имеет часы. который он выполняет. и выполняет сравнения в тех. Если есть различия, он запустится снова. (следующий цикл) Это не так просто, потому что оптимизированный, но весь ваш "угловой код" выполняется в этом цикле $digest. Многие директивы могут вызывать дайджест с scope.$apply(...) Это заставит часы любой ценности, которые они изменили, заметить и сделать свое дело.

Итак, ваш оригинальный вопрос. Это анти-паттерн? Абсолютно нет, если вы знаете, как его использовать. Хотя я бы просто использовал модель нг. Просто потому, что в нем было 1,2.10+ итераций довольно умных людей, работающих над ним... Все остальные "реактивные возможности" вашего приложения могут обрабатываться $q, $timeout и тому подобным.

Я думаю, что у них всех есть свое место, и для меня было бы трудно сказать прекратить использовать один для других.

Привязка данных всегда должна использоваться для синхронизации вашей модели данных с изменениями в представлении. Я думаю, что мы все можем согласиться с этим.

Я думаю, что использование часов на контроллере для запуска некоторых действий, основанных на изменении данных, полезно. Как смотреть сложную модель данных, чтобы вычислить промежуточную сумму для счета. Или смотреть модель, чтобы вызвать ее как грязную.

Я использовал широковещание / излучение / включение при отправке сообщения или указание на некоторые изменения из одной области в другую, которые могут находиться на расстоянии нескольких слоев. Я создал пользовательскую директиву, в которой событие широковещания использовалось как ловушка для выполнения некоторых действий в контроллере.

Другие вопросы по тегам