Aggiungi componenti Ember a Discourse

Nell’ultimo tutorial ho mostrato come configurare sia la parte lato server che quella lato client di Discourse per rispondere a una richiesta.

Ora consigliamo di leggere la documentazione sui componenti Ember: Introducing Components - Components - Ember Guides

Vecchio tutorial

In questo tutorial, creerò un nuovo componente Ember come modo per incapsulare codice JavaScript di terze parti. Sarà simile a un video YouTube che ho realizzato tempo fa, che potreste trovare informativo; questa volta, però, sarà specifico per Discourse e per come organizziamo i file nel nostro progetto.

Perché i componenti?

Handlebars è un linguaggio piuttosto semplice e invitante. Si tratta di HTML normale con alcune parti dinamiche. È facile da imparare e ottimo per la produttività, ma meno adatto al riutilizzo del codice. Se state sviluppando un’applicazione grande come Discourse, scoprirete di voler riutilizzare spesso gli stessi elementi.

I componenti sono la soluzione di Ember a questo problema. Creiamo un componente che visualizzi il nostro snack in modo più elegante.

Creazione di un nuovo componente

I componenti devono avere un trattino nel nome. Sceglierò fancy-snack come nome per questo. Creiamo il nostro template:

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>

Ora, per utilizzare il nostro componente, sostituiremo il nostro template esistente admin/snack con questo:

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

{{fancy-snack snack=model}}

Ora possiamo riutilizzare il nostro componente fancy-snack in qualsiasi altro template, passando semplicemente il modello come richiesto.

Aggiunta di codice JavaScript personalizzato

Oltre al riutilizzo, i componenti in Ember sono ottimi per aggiungere in modo sicuro codice JavaScript personalizzato, jQuery e altro codice esterno. Vi danno il controllo su quando il componente viene inserito nella pagina e quando viene rimosso. Per fare ciò, definiamo un Ember.Component con del codice:

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();
  },
});

Se aggiungete il codice sopra e aggiornate la pagina, vedrete che il nostro snack avrà un’animazione con uno sfondo giallo che si dissolve lentamente.

Spieghiamo cosa sta succedendo:

  1. Quando il componente viene renderizzato nella pagina, viene chiamato didInsertElement.

  2. La prima riga di didInsertElement (e di willDestroyElement) è this._super(), che è necessaria perché stiamo estendendo Ember.Component.

  3. L’animazione viene eseguita utilizzando la funzione animate di jQuery.

  4. Infine, l’animazione viene annullata nel hook willDestroyElement, che viene chiamato quando il componente viene rimosso dalla pagina.

Potreste chiedervi perché ci preoccupiamo di willDestroyElement; la ragione è che in un’applicazione JavaScript di lunga durata come Discourse è importante pulire dopo di sé, per evitare di perdere memoria o lasciare processi in esecuzione. In questo caso, fermiamo l’animazione, il che dice a eventuali timer jQuery che non devono più scattare poiché il componente non è più visibile nella pagina.

Da dove continuare

L’ultimo tutorial di questa serie tratta i test automatizzati.


Questo documento è sottoposto a controllo versione: suggerite modifiche su GitHub.

18 Mi Piace

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

1 Mi Piace

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 Mi Piace

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 Mi Piace

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 Mi Piace

Sarebbe bello vedere qui una documentazione aggiornata, che potrebbe rimandare alla documentazione di componente Glimmer.

3 Mi Piace