Añadir componentes Ember a Discourse

En el tutorial anterior mostré cómo configurar las partes del lado del servidor y del lado del cliente de Discourse para responder a una solicitud.

Ahora recomendamos leer la documentación de componentes de Ember: Introducing Components - Components - Ember Guides

Tutorial antiguo

En este tutorial, voy a crear un nuevo Componente Ember como una forma de encapsular Javascript de terceros. Esto será similar a un video de YouTube que hice hace algún tiempo, el cual podrías encontrar informativo, solo que esta vez es específico para Discourse y cómo organizamos los archivos en nuestro proyecto.

¿Por qué Componentes?

Handlebars es un lenguaje de plantillas bastante simple. Es solo HTML normal junto con algunas partes dinámicas. Esto es fácil de aprender y excelente para la productividad, pero no tan bueno para la reutilización de código. Si estás desarrollando una aplicación grande como Discourse, descubrirás que quieres reutilizar algunas de las mismas cosas una y otra vez.

Los componentes son la solución de Ember para este problema. Creemos un componente que mostrará nuestro snack de una manera más agradable.

Creando un nuevo Componente

Los componentes deben tener un guion en su nombre. Elegiré fancy-snack como nombre para este. Creemos nuestra plantilla:

app/assets/javascripts/admin/templates/components/fancy-snack.hbs

<div class="fancy-snack-title">
  <h1>{{snack.name}}</h1>
</div>

<div class="fancy-snack-description">
  <p>{{snack.description}}</p>
</div>

Ahora, para usar nuestro componente, reemplazaremos nuestra plantilla admin/snack existente con esta:

app/assets/javascripts/admin/templates/snack.hbs

{{fancy-snack snack=model}}

Ahora podemos reutilizar nuestro componente fancy-snack en cualquier otra plantilla, simplemente pasando el modelo según sea necesario.

Añadiendo Código Javascript Personalizado

Además de la reutilización, los componentes en Ember son excelentes para añadir código Javascript, jQuery y otro código externo de forma segura. Te da control sobre cuándo se inserta el componente en la página y cuándo se elimina. Para hacer esto, definimos un Ember.Component con algo de código:

app/assets/javascripts/admin/components/fancy-snack.js

export default Ember.Component.extend({
  didInsertElement() {
    this._super();
    this.$().animate({ backgroundColor: "yellow" }, 2000);
  },

  willDestroyElement() {
    this._super();
    this.$().stop();
  },
});

Si añades el código anterior y actualizas la página, verás que nuestro snack tiene una animación de un fondo amarillo que se desvanece lentamente.

Expliquemos lo que está sucediendo aquí:

  1. Cuando el componente se renderiza en la página, llamará a didInsertElement

  2. La primera línea de didInsertElement (y willDestroyElement) es this._super() lo cual es necesario porque estamos subclasificando Ember.Component.

  3. La animación se realiza utilizando la función animate de jQuery .

  4. Finalmente, la animación se cancela en el hook willDestroyElement, que se llama cuando el componente se elimina de la página.

Quizás te preguntes por qué nos importa willDestroyElement en absoluto; la razón es que en una aplicación Javascript de larga duración como Discourse es importante limpiar lo que usamos, no sea que tengamos fugas de memoria o dejemos cosas ejecutándose. En este caso detenemos la animación, lo que indica a cualquier temporizador de jQuery que no necesita activarse más ya que el componente ya no es visible en la página.

Por dónde seguir

El tutorial final de esta serie cubre las pruebas automatizadas.


Este documento está controlado por versiones: sugiere cambios en github.

17 Me gusta

Hi, how do i extend a discourse component thru a plugin? Can you give me some points. Thanks

Generally we prefer you don’t extend Discourse plugins, and you stick to plugin outlets or using the widget decoration API to add stuff.

But if you must, you can create an initializer and use Ember’s extend code. Here’s an example that extends an Ember object.

4 Me gusta

Tried with initializer but didnt worked. What i actually want to do is to add 2 more classNames and some actions:

import { withPluginApi } from 'discourse/lib/plugin-api';

function initializeComponentTopicList(api) {
  // extend component from jsapp/components/topic-list.js.es6
  const TopicList = api.container.lookupFactory('component:topic-list');

  TopicList.extend({
    classNames: ['topic-list', 'round', 'table'],
    actions: {
        clickMe: function() {
            console.log('click');
        }
    }
  });
};

export default {
  name: "extend-for-component-topic-list",

  initialize() {
    withPluginApi('0.1', initializeComponentTopicList);
  }
};

And by “didnt worked”, i mean that topic list completly disappeared from page.
Thank you

Were there any logs in the console?

Nope, no logs at all. However i managed to fix it this way. I hope it will help someone.

import { default as TopicList } from 'discourse/components/topic-list';
import { withPluginApi } from 'discourse/lib/plugin-api';

function initializeComponentTopicList(api) {
  TopicList.reopen({
    classNames: ['topic-list', 'round', 'table'],
  });
};

export default {
  name: "extend-for-component-topic-list",

  initialize() {
    withPluginApi('0.1', initializeComponentTopicList);
  }
};
3 Me gusta

I just ran into the same problem. Add the following as a css/html customisation and observe empty user cards:

<script type="text/discourse-plugin" version="0.5">
    api.container.lookupFactory('component:user-card-contents')
</script>
2 Me gusta

Sería bueno ver documentación actualizada aquí, que podría apuntar a la documentación del componente Glimmer.

3 Me gusta