¿Cuál es una buena manera de ‘conectar’ los controles de IU generados por JavaScript entre sí y definir sus interacciones?

Hm, si entiendo lo que estás diciendo, tienes un conjunto de widgets creados en el servidor, que se traducen en algo en el lado del cliente como Javascript ofuscado. Presumiblemente, desearía poder hacer cosas como alternar el texto de una etiqueta al presionar un botón, usando ButtonRenderer y LabelTextWidget en lugar de JavaScript de scripting personalizado (que puede que ni siquiera funcione cuando recompila el código de su servidor).

Vería la documentación de su lenguaje del lado del servidor para crear + despachar eventos y manejarlos. Esa suele ser la mejor manera de tratar con múltiples componentes. Por ejemplo, si su botón genera eventos ACTION cada vez que el usuario hace clic en él (o usa enter en su teclado), puede capturar el evento desde la etiqueta y modificar el texto. Entonces algo como:

  botón = nuevo ButtonRenderer ();
 etiqueta = nueva etiqueta ();
 label.handler.eventListen (button, ButtonRenderer.Event.ACTION, function (b) {
   label.text = b.text;
 });

Como parece ser el caso con otros que responden esta pregunta, no estoy del todo claro si lo entiendo, pero en la medida en que lo hago, aquí está mi respuesta:

Recomiendo usar pubsub y posiblemente el patrón de mediador para facilitar la comunicación entre widgets sin requerir que los widgets se comuniquen entre sí directamente. El objetivo aquí es evitar que widget foo llame a un método de barra de widgets cuando algo interesante le sucede a widget foo.

Puede evitar esto haciendo que un widget para “publicar” un tema cuando le ocurra algo interesante. Después de eso, tiene dos opciones: puede configurar la barra de widgets para “suscribirse” a ese tema y reaccionar en consecuencia, o puede hacer que un objeto mediador se suscriba al tema y llame al método apropiado en la barra de widgets.

(Con suerte) lo expliqué mejor en la presentación que hice en la Conferencia Boston jQuery 2010 ( http://www.slideshare.net/rmurph …); También es posible que desee ver un breve screencast que hice sobre el concepto de pubsub ( )

Si bien ciertamente puede tener widgets suscribiéndose a temas directamente, soy un gran admirador de usar un mediador como un puente de comunicación entre widgets, ya que permite que los widgets simplemente expongan una API sin preocuparse por cómo se usa esa API.

Muchas respuestas excelentes aquí que brindan enfoques muy diferentes a este problema. Diría que como ingenieros todavía no estamos seguros de cuál es el mejor enfoque para el desarrollo de la interfaz de usuario en el navegador (representación del lado del servidor frente a MVC del lado del cliente, widgets desacoplados con módulos de evento / estado, etc.). No estoy seguro de tener la mejor respuesta a la pregunta, pero básicamente paso el 80% de mi tiempo en el trabajo / en casa tratando de mejorar la interfaz de usuario en yelp. También adoptamos el enfoque de representación del lado del servidor y solo usamos JavaScript para aplicar el comportamiento. Estas son las cosas en las que pienso al desarrollar una interfaz de usuario basada en navegador.

Voy a construir pequeños módulos encapsulados que manejan pequeñas unidades de trabajo llamadas componentes. Al formar un árbol de estos componentes (a través de subcomponentes encapsulados) puede permitir que los componentes se comuniquen entre sí. Para ayudar a desacoplar estos componentes, cada componente tiene un elemento sobre el que actúa y solo asume la estructura dentro de su contenedor (nunca haga referencia a elementos fuera de su contenedor). Cada componente tiene un conjunto de eventos que puede publicar, que es la única forma de notificación que proporcionan. Los componentes principales pueden suscribirse a eventos de subcomponentes y notificar a los hermanos que deben cambiar algo. Por ejemplo, si tenía un componente de conjunto de radio y un componente de área de texto que se actualiza en función de la selección de radio, en lugar de que el área de texto se una al conjunto de radio, cree un componente principal (cuyo contenedor contenga el conjunto de radio + área de texto) que representa el evento. En el componente principal, verá un código que dice “cuando la radio active el evento de cambio, notifique al área de texto de lo que debería cambiar su valor”. Con este patrón, los componentes hermanos no saben realmente acerca de sus hermanos y pueden reutilizarse en otras estructuras de árbol. Mi ejemplo de radio / área de texto es increíblemente demasiado simple y la sobrecarga de esta abstracción puede no parecer útil, pero en casos más complejos con árboles muy profundos, esto puede ser muy útil.

Este patrón es muy similar a HMVC ( http://net.tutsplus.com/tutorial ). Tiene un árbol de controladores que tienen presentación (contenedor) y modelos (pueden realizar llamadas ajax para actualizar datos). Si está asumiendo la representación del lado del servidor, haga todo lo posible para seguir representando en el servidor; sus llamadas ajax deberían devolver json que contenga HTML. Desafortunadamente, esto significa que sus “modelos” están devolviendo datos de vista, pero teniendo en cuenta que el componente (módulo MVC) está directamente asociado con una presentación (supone el contenido dentro de un contenedor), creo que esto está bien. Sugiero representar el lado del servidor específicamente porque garantiza que sus plantillas no estén duplicadas y que i18n se realice en el servidor (evite enviar cadenas / lógica de traducción al navegador). Usar algo como el bigote ( http://mustache.github.com/ ) puede eliminar mi argumento para duplicar plantillas en todos los idiomas y eso podría valer la pena explorarlo.

Cuando se trata de aplicar componentes a su DOM estático, simplemente está adjuntando un conjunto de componentes de nivel superior a un conjunto de contenedores. Estos componentes de nivel superior crean subcomponentes, que luego pueden hacer subcomponentes, etc. Si está trabajando con una aplicación en la que todos los elementos de la página necesitan comunicarse entre sí, es posible que se encuentre en un escenario en el que tiene un solo componente de nivel superior, pero Creo que en la mayoría de los casos esto es raro.

A diferencia de muchos enfoques para el desarrollo de DOM UI, el acoplamiento es muy explícito. Está claro cómo ocurren los eventos en el DOM y no hay magia global de que si agrego este nombre de clase, de repente es un menú desplegable. Encuentro que los ingenieros a menudo no consideran que el DOM sea una variable global. Están de acuerdo con aplicar el comportamiento global a esa variable global, pero creo que todos podemos estar de acuerdo en que es global y, en general, no nos gustan los globales. En la mayoría de los sistemas de software evitamos el uso de globales, en el navegador tenemos pocas opciones, una variable global es nuestra capa de presentación, haga todo lo posible para no abusar de ella.

Con mi patrón descrito de un árbol de componentes, le sugiero que haga todo lo posible para no violar la Ley de Demeter ( http://en.wikipedia.org/wiki/Law ). Un componente en la profundidad 2 no debe interactuar con un componente en la profundidad 20 porque su árbol puede volverse difícil de refactorizar. Si un componente en el nivel 2 necesita saber sobre un evento en el nivel 20, cada componente en la rama debe publicar un evento apropiado (que probablemente no sea semánticamente el mismo evento en todos los niveles). El burbujeo de eventos es algo que trato de evitar usar en árboles muy grandes porque viola la ley.

La prueba del patrón descrito permite pruebas unitarias fáciles, cada componente se puede probar de forma independiente y las pruebas de integración se pueden realizar probando los componentes principales.

En términos de cómo hacer componentes, es realmente difícil de decir. En el pasado, he optado por una estructura basada en clases (una clase base proporciona el sistema de eventos básico), pero existen límites para la herencia única cuando se trata de construir comportamientos de IU: terminas con 100 opciones para cada componente o subclases que En realidad duplica el código. Dan Webb y Angus Croll de Twitter tienen algunos buenos comentarios sobre el uso de mixins funcionales para conectar comportamientos cuando los necesita ( https://speakerdeck.com/u/angusc ). Por ejemplo, puede tener un área de texto y un “complemento”, un límite en el número de caracteres, el comportamiento del texto del marcador de posición “complemento”, etc. Creo que ese modelo es muy atractivo y es algo que probablemente usaré en el futuro.

Si no sigue mi patrón y baja al concepto de modelo / estado más desacoplado, le recomiendo algo que hicimos en yelp llamado hub. Es un objeto que tiene dos funciones básicas: suscribirse / publicar. Parece ser increíblemente similar a un módulo pubsub básico, pero tiene una propiedad importante de que el último valor publicado se almacena realmente en el concentrador. Si un nuevo suscriptor se suscribe al centro y hay un valor anterior, el centro publica ese valor de inmediato. Esto es similar a $ .ready: si llega “demasiado tarde” (después de cargar el dom), aún se le llama. Vea el centro aquí: https://github.com/Yelp/thehub . Utilizamos mucho el centro en Yelp, pero hemos encontrado que los centros a veces se vuelven desordenados, especialmente si son objetos globales en los que las personas tienen valores fijos. También es muy importante que reconozca que no es un sistema de eventos, específicamente almacena el estado.