angularjs - que - ¿Es un antipattern usar $ watch de angular en un controlador?
ng-hide (3)
En mi interminable búsqueda de hacer las cosas de manera angular "adecuada", he estado leyendo mucho sobre cómo hacer que los controladores observen los cambios en los modelos que se mantienen en los servicios angulares.
Algunos sitios dicen que usar $ watch en un controlador es categóricamente incorrecto:
NO use $ watch en un controlador. Es difícil de probar y completamente innecesario en casi todos los casos. Use un método en el alcance para actualizar los valores que el reloj estaba cambiando en su lugar.
Others parecen estar de acuerdo con eso siempre y cuando limpies tu cuerpo:
La función $ watch en sí devuelve una función que desvinculará el $ watch cuando se llame. Entonces, cuando $ watch ya no es necesario, simplemente llamamos a la función devuelta por $ watch.
Hay preguntas de SO y otros sitios de buena reputación que parecen decir que usar un $ watch en un controlador es una excelente manera de notar los cambios en un modelo mantenido con servicio angular.
El sitio https://github.com/angular/angular.js/wiki/Best-Practices , que creo que podemos darle un poco más de peso, dice claramente que $ scope. $ Watch debería reemplazar la necesidad de eventos. Sin embargo, para los SPA complejos que manejan más de 100 modelos y puntos finales REST, la elección de usar $ watch para evitar eventos con $broadcast/$emit
puede terminar con muchos relojes. Por otro lado, si no usamos $ watch, para aplicaciones no triviales terminamos con toneladas de espaguetis de eventos.
¿Es esto una situación de perder / perder? ¿Es una elección falsa entre eventos y relojes? Sé que puedes usar el enlace bidireccional para muchas situaciones, pero a veces solo necesitas una forma de escuchar los cambios.
EDITAR
El comentario de Ilan Frumer me hizo replantearme lo que estoy preguntando, así que tal vez en lugar de solo preguntar si es subjetivamente bueno / malo usar un $ watch en un controlador, permítame hacer las preguntas de esta manera:
¿Qué implementación es probable que cree un cuello de botella de rendimiento primero? Hacer que los controladores escuchen los eventos (que deben haber sido emitidos / emitidos), o que configuren $watch
-es en los controladores. Recuerde, la aplicación a gran escala.
¿Qué implementación crea primero un dolor de cabeza de mantenimiento: $watch
-es o events? Podría decirse que hay un acoplamiento (apretado / suelto) de cualquier manera ... los observadores de eventos necesitan saber qué escuchar, y $watch
en valores externos (como MyDataService.getAccountNumber()
), ambos necesitan saber acerca de cosas que suceden fuera de su $ alcance.
** EDITAR más de un año después **
Angular ha cambiado / mejorado mucho desde que hice esta pregunta, pero todavía tengo +1, así que pensé en mencionar que al mirar el código del equipo angular, veo un patrón cuando se trata de observadores en los controladores (o Directivas donde hay un ámbito que se destruye):
$scope.$on(''$destroy'', $scope.$watch(''scopeVariable'', functionIWantToCall));
Lo que esto toma es lo que la función $ watch devuelve, una función que se puede llamar para anular el registro del observador, y se la da al controlador de eventos cuando se destruye el controlador. Esto limpia automáticamente al observador.
Ya sea que los relojes en los controladores tengan o no un código, si los usa, creo que el uso de este patrón por parte del equipo angular debería servir como una fuerte recomendación sobre cómo usarlos.
¡Gracias!
Creo que todos tienen su lugar adecuado y, para mí, sería difícil decir que dejen de usar uno para los demás.
El enlace de datos siempre se debe usar para mantener su modelo de datos sincronizado con los cambios de la vista. Creo que todos podemos estar de acuerdo en eso.
Creo que es útil usar un reloj en un controlador para activar alguna acción basada en un cambio de datos. Como ver un modelo de datos complejo para calcular el total acumulado de una factura. O viendo a una modelo dispararla como sucia.
He utilizado broadcast / emit / on al enviar un mensaje o una indicación de algún cambio de un ámbito a otro que puede estar a varias capas de distancia. He creado una directiva personalizada donde un evento de transmisión se ha utilizado como un gancho para tomar alguna acción en un controlador.
Todo lo que el enlace de datos bidireccional es, es un $watch
en cualquier propiedad de alcance que le dé a ng-model
, que tiene un controlador que permite otras directivas como input
, y form
sincronizar el valor de ng-model
para representar la vista en un cambio. Lo cual es detectado por su registro de eventos en el DOM.
En esencia, $watch
ng-model
compara el valor en el modelo, con el valor que tiene internamente. El valor que tiene internamente se establece mediante directivas de apoyo (entrada, formulario, etc.).
En mi humilde opinión, los únicos "eventos" a los que debe reaccionar en una aplicación angular son creados por el usuario (es decir, eventos DOM). Estos se resuelven con directivas en el DOM y ng-model
vinculando al ..model. También, naturalmente, hay async, para el cual angular proporciona $q
para los cuales las devoluciones de llamada invocan un $ digest.
En cuanto a rendimiento, lo dice muy bien en los documentos angulares. Se ejecuta en cada $digest
. Así que hazlo rápido ¿Cuál es cada $digest
? Angular atraviesa todos tus ámbitos activos. Cada alcance tiene relojes. que ejecuta. y realiza comparaciones en esos. Si hay diferencias, se ejecutará de nuevo. (el siguiente ciclo) No es tan simple porque está optimizado pero todo su "código angular" se ejecuta en este bucle $ digest. Muchas directivas pueden invocar un compendio con scope.$apply(...)
Eso hará que los relojes de cualquier valor cambien para notar y hacer lo suyo.
Así que tu pregunta original. ¿Es un anti-patrón? Absolutamente no, si sabes cómo usarlo. Aunque acabo de usar ng-model. Solo porque ha tenido 1.2.10+ iteraciones de personas muy inteligentes que trabajan en él ... Todas las otras "reactividades" de su aplicación pueden manejarse con $ q, $ timeout y similares.
Utilizo ambos, porque, sinceramente, los veo como herramientas diferentes para diferentes problemas.
Daré un ejemplo de una aplicación que construí. Tenía un servicio WebSocket complejo que recibía modelos de datos dinámicos de un servidor de socket web. Al servicio en sí no le importa cómo se ve la modelo, pero, por supuesto, el controlador sí lo hace.
Cuando se inicia el controlador, configura un $watch
en el objeto de datos de servicio para que sepa cuándo ha llegado el objeto de datos en particular (como esperar a que exista Service.data.foo
. Tan pronto como ese modelo entró en existencia, fue capaz de vincularse a ella y encapsularla en un enlace de datos bidireccional, el reloj se volvió obsoleto y se destruyó.
Por otro lado, el servicio también era responsable de transmitir ciertos eventos, porque a veces el cliente recibía comandos literales del servidor. Por ejemplo, el Servidor podría solicitar que el cliente envíe algunos metadatos que se almacenaron en el ''$ rootScope'' en toda la aplicación. se .on()
un .on()
en el paso $rootScope
durante el module.run()
para escuchar esos comandos del servidor, recopilar la información necesaria de otros servicios y llamar al servicio WebSocket para enviar los datos según lo solicitado. Alternativamente, si hubiera hecho esto usando $watch()
, habría necesitado configurar algún tipo de variable arbitraria para que lo viera, como las Solicitudes de metadataRequests
que necesitaría incrementar cada vez que recibiera una solicitud. Una broadcast
logra lo mismo sin tener que vivir en memoria permanente, como haría nuestra variable.
Esencialmente, uso un $watch()
cuando hay un valor específico que quiero ver cambiar (especialmente si necesito saber los valores de antes y después), y uso eventos si hay más condiciones de alto nivel que Se han cumplido que los controladores necesitan saber.
En lo que respecta al rendimiento, no podría decirle cuál será el primero en el cuello de botella, pero siento que pensar de esta manera le permitirá usar las fortalezas de cada función en las que son más fuertes. Por ejemplo, si usa $on()
lugar de $watch()
para buscar cambios en los datos, no tendrá acceso a los valores antes y después del cambio, lo que podría limitar lo que está tratando de hacer.