Se stai implementando un nuovo Modal, dai un’occhiata alla documentazione principale qui. Questo argomento descrive come migrare un Modal basato su controller esistente alla nuova API basata su Componenti.
In passato, Discourse utilizzava un’API basata su Controller Ember per il rendering dei modali. Per richiamare il modale, si passava una stringa con il nome del controller a showModal(). Sotto la superficie, questo faceva uso dell’API Route#renderTemplate di Ember, che è deprecata in Ember 3.x e verrà rimossa in Ember 4.x.
Per consentire a Discourse di aggiornarsi a Ember 4.x e oltre, abbiamo introdotto una nuova API basata su componenti per i modali. Questa nuova API abbraccia i pattern di progettazione ‘dichiarativi’ di Ember e mira a fornire una semantica pulita DDAU (data down actions up).
Passaggio 1: Spostare i file
Sposta il file JS del controller e il file del template nella directory /components/modal. Questo li rende un ‘componente co-locato’ che può essere importato come qualsiasi altro modulo JS.
Passaggio 2: Aggiornare il file JS
Quindi, aggiorna la definizione JS del componente per estendere @ember/component invece di @ember/controller [1]. Rimuovi il mixin ModalFunctionality e aggiorna qualsiasi utilizzo delle sue funzioni secondo la tabella seguente:
| Prima | Dopo |
|---|---|
flash() e clearFlash() |
Crea una proprietà flash nel tuo componente e passala all’argomento @flash di <DModal>. Di default l’avviso verrà stilizzato con la classe alert che è una copia della classe ‘error’, ma può essere sovrascritta usando l’argomento @flashType. |
showModal() |
Importa la funzione showModal da discourse/lib/show-modal |
azione closeModal |
Richiama l’argomento closeModal che viene passato automaticamente al tuo componente |
I Controller Modal di vecchio stile vivevano ‘per sempre’, il che significava che dovevamo pulire manualmente lo stato. Con la nuova API basata su Componenti, il componente verrà creato e distrutto quando il modale viene mostrato/nascosto. In molti casi ciò significa che i vecchi hook di ciclo di vita non sono più necessari.
Se hai ancora bisogno di logica basata sul ciclo di vita, usa questa tabella:
| Prima | Dopo |
|---|---|
onShow() |
Usa il ciclo di vita standard dei componenti Ember (init() o modificatore Ember) |
afterRender |
Usa il ciclo di vita standard dei componenti Ember (init() o modificatore Ember) |
beforeClose() |
Crea un wrapper attorno all’argomento @closeModal che viene passato al tuo componente. Passa un riferimento al tuo wrapper di chiusura a DModal come <DModal @closeModal={{this.myCloseModalWrapper}}> |
onClose() |
Usa il ciclo di vita standard dei componenti Ember (willDestroy() o modificatore Ember) |
Passaggio 3: Aggiornare il Template
Sostituisci il wrapper <DModalBody> con <DModal>. Aggiungi alcuni nuovi attributi:
- Passa attraverso l’argomento
@closeModalnuovo - Aggiungi una classe esplicita. Per corrispondere al vecchio comportamento, prendi il nome del file del controller e aggiungi
-modal.
Ad esempio, se il tuo controller modale si chiamava close-topic.js, la nuova chiamata a <DModal> avrebbe un aspetto simile a questo:
<DModal @closeModal={{@closeModal}} class="close-topic-modal">
Se la chiamata a DModalBody include altri argomenti, aggiornali in base alla tabella seguente:
| Prima | Dopo |
|---|---|
@title="title_key" |
@title={{i18n "title_key"}} |
@rawTitle="translated title" |
@title="translated title" |
@subtitle="subtitle_key" |
@subtitle={{i18n "subtitle_key"}} |
@rawSubtitle="translated subtitle" |
@subtitle="translated subtitle" |
@class |
@bodyClass |
@modalClass |
Usa la sintassi con parentesi angolari con attributo html regolare: <DModal class="blah"> |
@titleAriaElementId |
Usa la sintassi con parentesi angolari con attributo html regolare: <DModal aria-labelledby="blah"> |
@dismissable, @submitOnEnter, @headerClass |
Invariato |
Se c’era contenuto footer renderizzato dopo il vecchio componente <DModalBody>, usa il nuovo blocco nominato <:footer> per introdurlo all’interno di <DModal>. Quando si usano blocchi nominati, il contenuto del corpo deve essere avvolto in <:body></:body>. Ad esempio:
<DModal @closeModal={{@closeModal}}>
<:body>
Hello world, this is the content of the modal
</:body>
<:footer>
This is the footer content. A `.modal-footer` wrapper will be added
automatically
</:footer>
</DModal>
Passaggio 4: Aggiornare i punti di chiamata di showModal
In precedenza, i modali venivano renderizzati usando l’API showModal, che prendeva una stringa (il nome del controller) e una serie di opzioni. Ritornava un’istanza del controller che poteva essere manipolata:
import showModal from "discourse/lib/show-modal";
export default class extends Component {
showMyModal() {
const controller = showModal("my-modal", {
title: "My Modal Title",
modalClass: "my-modal-class",
model: { topic: this.topic },
});
controller.set("updateTopic", this.updateTopic);
});
}
Per renderizzare i nuovi Modali basati su componenti, dovresti iniettare il servizio ‘modal’ (o accedervi usando qualcosa come getOwner(this).lookup("service:modal")) e chiamare la funzione show().
show() prende un riferimento alla nuova classe Component come primo argomento. L’unica opzione ancora supportata è ‘model’, che può essere usata per passare tutti i dati/azioni richiesti per il tuo Modal.
Non verrà restituito alcun riferimento all’istanza del componente. Invece, show() restituisce una promise che verrà risolta quando il modale viene chiuso. La promise verrà risolta con qualsiasi dato che è stato passato a @closeModal.
import MyModal from "discourse/components/my-modal";
import { service } from "@ember/service";
export default class extends Component {
@service modal;
showMyModal() {
this.modal.show(MyModal, {
model: { topic: this.topic, updateTopic: this.updateTopic },
});
});
}
In alternativa, migra all’API dichiarativa descritta nella documentazione principale di DModal.
La funzionalità delle vecchie opzioni può essere replicata come segue:
Vecchia opzione showModal |
Soluzione |
|---|---|
admin |
n/d per componente - rimuovilo |
templateName |
n/d per componenti - rimuovilo |
title |
sposta in <DModal @title={{i18n "blah"}}> |
titleTranslated |
sposta in <DModal @title="blah">. Questo potrebbe essere calcolato in base ai dati da model se necessario |
modalClass |
sposta in <DModal class="blah"> |
titleAriaElementId |
sposta in <DModal aria-labelledby="blah"> |
panels |
Usa il blocco nominato <:headerBelowTitle> per implementare le tab nel tuo componente (esempio) |
model |
invariato |
Passaggio 5: Test
I test dovrebbero in gran parte rimanere gli stessi. Il problema più comune è:
-
I Modali non hanno più una classe predefinita basata sul loro nome. Le classi devono essere specificate esplicitamente nel template (vedi inizio del Passaggio 3)
-
Il wrapper
d-modalnon persiste più nel DOM quando il modale viene chiuso. Per verificare che tutti i modali siano chiusi, usa un controllo comeassert.dom('.d-modal').doesNotExist()
Profitto!
Il tuo modale dovrebbe ora funzionare come prima. Per sfruttare ulteriormente la nuova API, potresti voler considerare di sostituire le chiamate a showModal con una strategia dichiarativa, e convertire il tuo Modal in un componente Glimmer.
Esempi
Ecco alcuni commit di esempio che dimostrano la conversione di alcuni dei modali core di Discourse alla nuova API:
Questo documento è sotto controllo di versione - suggerisci modifiche su github.
I Classic Ember Components sono raccomandati in questa guida perché hanno fornito il percorso di migrazione più semplice dai Controller Ember. Ma per modali semplici, o se sei disposto a dedicare del tempo al refactoring, i moderni componenti Glimmer sono la scelta migliore. ↩︎