Componentes Ember Una inmersión profunda

Ember.js es un marco de JavaScript MVC que permite a los desarrolladores crear aplicaciones web ambiciosas. Aunque el MVC puro permite que un desarrollador separe las inquietudes, no le proporciona todas las herramientas y su aplicación necesitará otras construcciones. Hoy voy a hablar de uno de esos constructos. Los componentes de madera son esencialmente trozos reutilizables de la interfaz de usuario. Si no está familiarizado con Ember, consulte la sección Introducción a Ember.js o el curso Let's Learn Ember. En este tutorial, cubriremos la especificación de componentes web, aprenderemos cómo escribir un componente en Ember, hablaremos sobre la composición, explicaremos la diferencia entre una vista Ember y un componente Ember, y practicaremos la integración de complementos con componentes Ember.


Una palabra sobre los componentes web

Los componentes de Ember se basan en la especificación de componentes web de W3C. La especificación se compone de cuatro especificaciones más pequeñas; Plantillas, decoradores, DOM sombra, y elementos personalizados. De estos cuatro conceptos solo tres de ellos tienen especificaciones de endurecimiento, los decoradores son la excepción. Al tener las especificaciones en su lugar, los desarrolladores del marco han sido capaces de rellenar estas nuevas API antes de que los proveedores del navegador las implementen.

Hay varios conceptos importantes para comprender cuando se habla de componentes:

  • Los componentes no saben nada sobre el mundo exterior a menos que se pasen explícitamente
  • Los componentes deben tener una interfaz bien definida para el mundo exterior.
  • Los componentes no pueden manipular ningún JavaScript fuera del componente
  • Los componentes pueden transmitir eventos
  • Los elementos personalizados deben ir marcados con un guión.
  • JavaScript externo no puede manipular componentes

Los componentes web proporcionan una verdadera encapsulación para los widgets de UI. A continuación se muestra un diagrama de cómo funciona un componente en el nivel más básico..

Si bien Ember ha completado con éxito muchas especificaciones, los marcos como AngularJS, Dart, Polymer y Xtags tienen soluciones similares. La única advertencia aquí es que Ember y Angular actualmente no tienen estilos de alcance para el componente. Con el tiempo, estas soluciones de polyfill desaparecerán y los marcos adoptarán la implementación del proveedor del navegador. Este es un enfoque fundamentalmente diferente para el desarrollo, ya que podemos aprovechar las especificaciones futuras sin atarnos a funciones experimentales en los navegadores..


El componente más básico de Ember

Con nuestro conocimiento de los componentes web, implementemos el componente my-name básico desde arriba, pero en Ember. Comencemos descargando el Ember Starter Kit del sitio web de Ember. En el momento de este tutorial, la versión de Ember es 1.3.0. Una vez que lo hayas descargado, abre los archivos en tu editor favorito, borra todas las plantillas en index.html (indicado con el nombre de la plantilla de datos) y todo en app.js.

Lo primero que vamos a hacer es crear nuestra plantilla de componentes. Por el bien de este tutorial vamos a utilizar plantillas en línea. Haz esto escribiendo lo siguiente en tu index.html expediente. También necesitamos crear una nueva aplicación Ember en nuestro JavaScript.

   var App = Ember.Application.create ();

Notará que el nombre de la plantilla de datos tiene un nombre de ruta en lugar de una simple cadena. La razón por la que prefijamos el nombre de nuestro componente con "componentes /" es decirle a Ember que estamos tratando con una plantilla de componente y no con una plantilla de aplicación regular. También notará que el nombre del componente tiene el guión. Este es el espacio de nombres que mencioné en la especificación de componentes web. El espacio de nombres se realiza para que no tengamos colisiones de nombres con etiquetas existentes.

Si abrimos el navegador, no deberíamos ver nada diferente. La razón de esto es que aún no hemos colocado nada en nuestra plantilla de mi nombre. Cuidemos de eso.

... 

Ahora en el navegador deberías ver algo como la imagen de arriba. Todavía no hemos terminado, como puedes ver, en realidad no estamos imprimiendo un nombre. Como mencioné en la primera sección, los componentes deben exponer una interfaz bien definida al mundo exterior. En este caso, nos preocupa el nombre. Así que pasemos el nombre colocando un atributo de nombre en el componente my-name.

... 

Cuando actualices la página deberías ver "Hola, mi nombre es Chad". Todo esto con la escritura de una línea de JavaScript. Ahora que tenemos la sensación de escribir un componente básico, hablemos de la diferencia entre los componentes Ember y las vistas Ember.


Ember Components vs. Ember Views

Ember es un MVC, por lo que algunos pueden estar pensando: "¿Por qué no usar una vista para esto?" Esta es una pregunta legítima. Los componentes en realidad son una subclase de Ember.View, la mayor diferencia aquí es que las vistas generalmente se encuentran en el contexto de un controlador. Toma el ejemplo de abajo.

 App.IndexController = Ember.Controller.extend (myState: 'on'); App.IndexView = Ember.View.extend (click: function () var controller = this.get ('controller'), myState = controller.get ('myState'); console.log (controller) // El controlador instancia console.log (myState) // La cadena "on");
 

Las vistas normalmente se ubican detrás de una plantilla y convierten la entrada sin formato (clic, mouseEnter, mouseMove, etc.) en una acción semántica (openMenu, editName, hideModal, etc.) en un controlador o ruta. Otra cosa a destacar es que las plantillas también necesitan un contexto. Entonces, lo que termina sucediendo es que Ember infiere el contexto a través de convenciones de nomenclatura y la URL. Vea el diagrama de abajo.

Como puede ver, hay un nivel de jerarquía basado en la URL y cada nivel de esa jerarquía tiene su propio contexto que se deriva de las convenciones de nomenclatura..

Los componentes de Ember no tienen un contexto, solo conocen la interfaz que definen. Esto permite que un componente se represente en cualquier contexto, lo que lo hace desacoplado y reutilizable. Si el componente expone una interfaz, es el trabajo del contexto cumplir con esa interfaz. En otras palabras, si desea que el componente se reproduzca correctamente, debe proporcionarle los datos que está esperando. Es importante tener en cuenta que estos valores pasados ​​pueden ser tanto cadenas como propiedades enlazadas.

Cuando las propiedades enlazadas se manipulan dentro de un componente, esos cambios aún se propagan donde sea que se haga referencia en su aplicación. Esto hace que los componentes sean extremadamente potentes. Ahora que tenemos una buena comprensión de cómo los componentes son diferentes de las vistas, veamos un ejemplo más complejo que ilustra cómo un desarrollador puede componer múltiples componentes.


Composición de Componentes

Una cosa realmente buena de Ember es que está basada en conceptos de jerarquía de UI y esto es muy evidente con la composición de los componentes. A continuación se muestra un ejemplo de lo que vamos a hacer. Es una interfaz de usuario de chat de grupo simple. Obviamente, no voy a escribir un servicio de chat completo para alimentar la interfaz de usuario, pero podemos ver cómo podemos dividir la interfaz de usuario en componentes reutilizables y compuestos..

Veamos primero cómo vamos a dividir la IU en partes más pequeñas y más digestibles. Todo lo que podamos dibujar alrededor de un cuadro es un componente, con la excepción de las entradas de texto y botones en la parte inferior de la interfaz de usuario. Nuestro objetivo es poder configurar solo el componente en la capa externa, todo lo demás debería funcionar.

Empecemos creando un nuevo archivo html llamado chat.html y configurar todas las dependencias para Ember. Luego crea todas las plantillas.

       

Verá que los componentes se pueden anidar dentro de otros componentes. Esto hace que los componentes sean como legos que podemos ensamblar de la manera que queramos. Solo tenemos que escribir en la interfaz del componente..

Si ahora vamos a buscar en el navegador, no deberíamos ver mucho porque no tenemos ningún flujo de datos en el componente. También notará que aunque no haya datos, los componentes no arrojan un error. Lo único que realmente se procesa aquí es el área de entrada y el botón de envío. Esto se debe a que no dependen de lo que se pasa en.

Si observa detenidamente las plantillas, observará que hemos asignado un par de cosas en el componente de chat de grupo..

 

En este caso, estamos pasando el modelo desde el contexto de la IndexRuta como "mensajes" y hemos establecido la cadena de enviar mensaje Como la acción sobre el componente. La acción se utilizará para difundir cuando el usuario quiera enviar un nuevo mensaje. Cubriremos esto más adelante en el tutorial. La otra cosa que notará es que estamos configurando interfaces estrictas para los componentes anidados, todos los cuales están utilizando los datos transmitidos desde la interfaz de chat de grupo..

... 
    # mensaje en mensajes
  • chat-message username = message.twitterUserName message = message.text time = message.timeStamp
  • /cada
...

Como se mencionó anteriormente, puede pasar cadenas o propiedades enlazadas a componentes. Como regla general, use comillas al pasar una cadena, no use comillas al pasar una propiedad vinculada. Ahora que tenemos nuestras plantillas en su lugar, arrojemos algunos datos simulados.

 App = Ember.Application.create (); App.IndexRoute = Ember.Route.extend (model: function () return [id: 1, firstName: 'Tom', lastName: 'Dale', twitterUserName: 'tomdale', text: 'Creo que deberíamos retroceder viejo Tomster. Era increíble. ', timeStamp: Date.now () - 400000,, id: 2, FirstName:' Yehuda ', LastName:' Katz ', twitterUserName:' wycats ', texto:' That \ ' Es una buena idea. ', timeStamp: Date.now () - 300000,];);

Si vamos a ver esto en el navegador ahora, deberíamos ver un poco de progreso. Pero aún queda trabajo por hacer, principalmente para que las imágenes se muestren, formateen la fecha y puedan enviar un nuevo mensaje. Cuidemos de eso.

Con nuestro componente de usuario-avatar, queremos usar un servicio llamado Avatars.io para obtener el avatar de Twitter de un usuario basado en su nombre de usuario de Twitter. Veamos cómo se utiliza el componente de imagen de usuario en la plantilla..

  

Es un componente bastante simple pero notará que tenemos una propiedad vinculada llamada avatarUrl. Vamos a necesitar crear esta propiedad dentro de nuestro JavaScript para este componente. Otra cosa que notará es que estamos especificando el servicio del que queremos obtener el avatar. Avatars.io te permite obtener avatares sociales de Twitter, Facebook e Instagram. Podemos hacer este componente extremadamente flexible. Vamos a escribir el componente.

 App.UserAvatarComponent = Ember.Component.extend (avatarUrl: function () var username = this.get ('username'), service = this.get ('service'), availableServices = ['twitter', 'facebook' , 'instagram']; if (availableServices.indexOf (service)> -1) return 'http://avatars.io/' + service + '/' + username; return 'images / cat.png'; .property ('nombre de usuario', 'servicio'));

Como puede ver, para crear un nuevo componente solo seguimos la convención de nomenclatura de NAMEOFCOMPONENTComponent y extender Ember.Component. Ahora si volvemos al navegador deberíamos ver nuestros avatares.

Para encargarnos del formato de la fecha, usemos moment.js y escribamos un ayudante de Handlebars para dar formato a la fecha para nosotros.

 Ember.Handlebars.helper ('format-date', function (date) return moment (date) .fromNow (););

Ahora todo lo que tenemos que hacer es aplicar el asistente a nuestro componente de marca de tiempo.

 

Ahora deberíamos tener un componente que formatee fechas en lugar de las marcas de tiempo de la época de Unix.

Aunque podemos hacer uno mejor. Estas marcas de tiempo deberían actualizarse automáticamente con el tiempo, así que vamos a hacer que nuestro componente de marca de tiempo haga exactamente eso.

 App.TimeStampComponent = Ember.Component.extend (startTimer: function () var currentTime = this.get ('time'); this.set ('time', currentTime - 6000); this.scheduleStartTimer ();, scheduleStartTimer: function () this._timer = Ember.run.later (this, 'startTimer', 6000); .on ('didInsertElement'), killTimer: function () Ember.run.cancel (this._timer) ; .on ('willDestroyElement'));

Un par de puntos a tener en cuenta aquí. Uno es el en() Sintaxis declarativa del manejador de eventos. Esto fue introducido en Ember antes de la versión 1.0. Hace exactamente lo que usted cree que hace, cuando el componente de marca de tiempo se inserta en el DOM, scheduleStartTime se llama. Cuando el elemento está a punto de ser destruido y limpiado el killTimer Se llamará método. El resto del componente solo indica el tiempo para actualizar cada minuto..

La otra cosa que notará es que hay varias llamadas a Ember.run. En Ember hay un sistema de cola de espera, normalmente conocido como el bucle de ejecución, que se vacía cuando se modifican los datos. Esto se hace básicamente para fusionar cambios y hacer el cambio una vez. En nuestro ejemplo vamos a utilizar Ember.run.later para ejecutar el startTimer Método cada minuto. También usaremos Ember.run.cancel para encolar el temporizador. Se trata esencialmente de los propios métodos de intervalo de inicio y parada de Ember. Se necesitan para mantener sincronizado el sistema de colas. Para más información sobre el ciclo de ejecución, sugiero leer el artículo de Alex Matchneer "Todo lo que nunca quisiste saber sobre el circuito de ejecución de Ember".

Lo siguiente que debemos hacer es configurar la acción para que cuando el usuario haga clic en Enviar, se cree un nuevo mensaje. A nuestro componente no debe importarle cómo se crean los datos, solo debe difundir que el usuario ha intentado enviar un mensaje. Nuestro IndexRuta Será responsable de tomar esta acción y convertirse en algo significativo..

 App.GroupChatComponent = Ember.Component.extend (message: ", acciones: submit: function () var message = this.get ('message') .trim (), conversation = this. $ ('Ul') [0]; // Obtiene el valor de 'acción' // y envía la acción con el mensaje this.sendAction ('acción', mensaje); // Cuando finaliza el ciclo de ejecución de Ember // desplácese hasta el Ember inferior. run.schedule ('afterRender', function () conversation.scrollTop = conversation.scrollHeight;); // Restablecer el campo de mensaje de texto this.set ('message', "););
 
input type = "text" placeholder = "Enviar mensaje nuevo" value = message input type = "submit" value = "Send"

Dado que el componente de chat grupal posee el botón de entrada y envío, debemos reaccionar al usuario haciendo clic en enviar en este nivel de abstracción. Cuando el usuario haga clic en el botón Enviar, ejecutará la acción Enviar en la implementación de nuestro componente. Dentro del controlador de la acción de envío, obtendremos el valor del mensaje, que se establece en la entrada de texto. Luego enviaremos la acción junto con el mensaje. Finalmente, restableceremos el mensaje a una cadena en blanco..

La otra cosa rara que ves aquí es la Ember.run.schedule Método que se llama. Una vez más, este es el ciclo de ejecución de Ember en acción. Notará que la programación toma una cadena como primer argumento, en este caso "afterRender". En realidad, Ember tiene varias colas diferentes que administra, y es una de ellas. Entonces, en nuestro caso, estamos diciendo que cuando se realiza el envío del mensaje haciendo manipulaciones y después de que se haya vaciado la cola de procesamiento, llame a nuestra devolución de llamada. Esto desplazará nuestro ul al final para que el usuario pueda ver el nuevo mensaje después de cualquier manipulación. Para obtener más información sobre el ciclo de ejecución, sugiero leer el artículo de Alex Matchneer "Todo lo que nunca quisiste saber sobre el circuito de ejecución de Ember".

Si vamos al navegador y hacemos clic en el botón enviar, recibimos un error muy agradable de Ember que dice "Error no detectado: Nada manejó el evento 'enviar mensaje'. Esto es lo que esperamos porque no le hemos dicho a nuestra aplicación cómo a la reacción a este tipo de eventos. Vamos a arreglar eso.

 App.IndexRoute = Ember.Route.extend (/ *… * / actions: sendMessage: function (message) if (message! == ") console.log (message););

Ahora, si volvemos al tipo de navegador en la entrada del mensaje y pulsamos enviar, deberíamos ver el mensaje en la consola. Por lo tanto, en este punto, nuestro componente está acoplado de manera flexible y, en relación con el resto, nuestra aplicación. Hagamos algo más interesante con esto. Primero vamos a crear una nueva Objeto.beber Trabajar como modelo para un nuevo mensaje..

 App.Message = Ember.Object.extend (id: 3, firstName: 'Chad', lastName: 'Hietala', twitterUserName: 'chadhietala', text: null, timeStamp: null);

Entonces cuando el enviar mensaje acción ocurre vamos a querer rellenar el texto y TimeStamp En el campo de nuestro modelo de mensaje, cree una nueva instancia del mismo y luego insértela en la colección de mensajes existente..

 App.IndexRoute = Ember.Route.extend (/ *… * / actions: sendMessage: function (message) var user, messages, newMessage; if (message! == ") messages = this.modelFor ('index '), newMessage = App.Message.create (text: message, timeStamp: Date.now ()) messages.pushObject (newMessage););

Cuando volvamos al navegador, ahora deberíamos poder crear nuevos mensajes..

Ahora tenemos varios trozos reutilizables diferentes de UI que podemos colocar en cualquier lugar. Por ejemplo, si necesita usar un avatar en algún otro lugar de su aplicación Ember, podemos reutilizar el componente usuario-avatar.

 

Envolviendo complementos de jQuery

En este punto, probablemente se esté preguntando "¿Qué pasa si quiero usar algún complemento jQuery en mi componente?" No hay problema. Para mayor brevedad, modifiquemos nuestro componente usuario-avatar para mostrar una sugerencia de herramienta cuando pasamos el mouse sobre el avatar. Elegí usar el complemento de herramientas jQuery para manejar la información sobre herramientas. Vamos a modificar el código existente para utilizar tooltipster..

Primero, agreguemos archivos correctos a nuestro chat.html y modificar el componente de avatar de usuario existente.

... ...  ... 

Y luego nuestro JavaScript:

 App.UserAvatarComponent = Ember.Component.extend (/ *… * / setupTooltip: function () this. $ ('.Avatar') .tooltipster (animation: 'fade'); .on ('didInsertElement' ), destroyTooltip: function () this. $ ('.avatar') .tooltipster ('destroy'); .on ('willDestroyElement'));

Una vez más, vemos la sintaxis declarativa del detector de eventos, pero por primera vez vemos esto. $. Si está familiarizado con jQuery, esperaría que estuviéramos consultando todos los elementos con la clase de 'avatar'. Este no es el caso en Ember porque se aplica el contexto. En nuestro caso, solo estamos buscando elementos con la clase de 'avatar' en el componente usuario-avatar. Es comparable al método de búsqueda de jQuery. En la destrucción del elemento, deberíamos desenlazar el evento de desplazamiento en el avatar y limpiar cualquier funcionalidad, esto se hace pasando 'destruir' a la herramienta tipster. Si vamos al navegador, actualizamos y colocamos una imagen, deberíamos ver el nombre de usuario del usuario..


Conclusión

En este tutorial, realizamos una inmersión profunda en los componentes de Ember y mostramos cómo puede tomar fragmentos reutilizables de la interfaz de usuario para generar compuestos más grandes e integrar los complementos de jQuery. Nos fijamos en cómo los componentes son diferentes de las vistas en Ember. También cubrimos la idea de la programación basada en interfaces cuando se trata de componentes. Con suerte, pude arrojar algo de luz no solo sobre los Componentes Ember sino también sobre los Componentes Web y hacia dónde se dirige la Web..