Aggiungi componenti Ember a Discourse

Nel tutorial precedente ho mostrato come configurare sia il lato server che il lato client di Discourse per rispondere a una richiesta.

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

Tutorial precedente

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

Perché i Componenti?

Handlebars è un linguaggio di templating piuttosto semplice. È solo HTML normale insieme ad alcune parti dinamiche. È facile da imparare e ottimo per la produttività, ma non così eccezionale per il riutilizzo del codice. Se stai sviluppando un’applicazione di grandi dimensioni come Discourse, scoprirai di voler riutilizzare alcune delle stesse cose più e più volte.

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

Creazione di un nuovo Componente

I componenti devono avere un trattino nel loro 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 usare il nostro componente, sostituiremo il nostro template admin/snack esistente 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 alla riutilizzabilità, i componenti in Ember sono ottimi per aggiungere in modo sicuro Javascript personalizzato, jQuery e altro codice esterno. Ti 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 aggiungi il codice sopra e aggiorni la pagina, vedrai che il nostro snack ha un’animazione di uno sfondo giallo che svanisce lentamente.

Spieghiamo cosa sta succedendo qui:

  1. Quando il componente viene renderizzato sulla pagina, chiamerà didInsertElement

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

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

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

Potresti chiederti perché ci preoccupiamo di willDestroyElement; il motivo è che in un’applicazione Javascript di lunga durata come Discourse è importante fare pulizia, per non incorrere in perdite di memoria o lasciare cose in esecuzione. In questo caso interrompiamo l’animazione, il che dice a qualsiasi timer jQuery che non è più necessario che si attivi poiché il componente non è più visibile sulla pagina.

Dove andare da qui

Il tutorial finale di questa serie copre i test automatizzati.


Questo documento è controllato tramite versione - suggerisci modifiche su github.

17 Mi Piace

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