Se stai implementando un nuovo Modal, consulta la 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 Ember-Controller per il rendering dei modali. Per invocare il modal, si passava una stringa contenente il nome del controller a showModal(). Sotto il cofano, questo faceva uso dell’API Route#renderTemplate di Ember, che è deprecata in Ember 3.x e verrà rimossa in Ember 4.x.
Per permettere a Discourse di aggiornarsi a Ember 4.x e versioni successive, abbiamo introdotto una nuova API basata su componenti per i modali. Questa nuova API abbraccia i modelli di progettazione ‘dichiarativi’ di Ember e mira a fornire una semantica DDAU (data down actions up) pulita.
Passo 1: Sposta i file
Sposta il file JS del controller e il file del template nella directory /components/modal. Questo li rende un ‘colocated component’ che può essere importato esattamente come qualsiasi altro modulo JS.
Passo 2: Aggiorna il file JS
Quindi, aggiorna la definizione del componente JS per estendere @ember/component invece di @ember/controller [1]. Rimuovi il mixin ModalFunctionality e aggiorna tutti gli utilizzi delle sue funzioni secondo la tabella sottostante:
| 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 utilizzando l’argomento @flashType. |
showModal() |
Importa la funzione showModal da discourse/lib/show-modal |
Azione closeModal |
Invoca l’argomento closeModal che viene automaticamente passato al tuo componente |
I Modal Controller di vecchio stile vivevano ‘per sempre’, il che significava che dovevamo pulire manualmente lo stato. Con la nuova API basata su componenti, il componente viene creato e distrutto quando il modal viene mostrato/nascosto. In molti casi, questo significa che i vecchi hook di ciclo di vita non sono più necessari.
Se hai ancora bisogno di una logica basata sul ciclo di vita, usa questa tabella:
| Prima | Dopo |
|---|---|
onShow() |
Usa il ciclo di vita standard dei componenti Ember (init() o un modificatore Ember) |
afterRender |
Usa il ciclo di vita standard dei componenti Ember (init() o un 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 un modificatore Ember) |
Passo 3: Aggiorna il template
Sostituisci il wrapper <DModalBody> con <DModal>. Aggiungi alcuni nuovi attributi:
- Passa il nuovo argomento
@closeModal - Aggiungi una classe esplicita. Per corrispondere al comportamento precedente, prendi il nome del file del controller e aggiungi
-modal.
Ad esempio, se il tuo controller modal si chiamava close-topic.js, la nuova invocazione di <DModal> sarebbe simile a questa:
<DModal @closeModal={{@closeModal}} class="close-topic-modal">
Se l’invocazione di DModalBody include altri argomenti, aggiornali in base alla tabella sottostante:
| 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 un attributo HTML regolare: <DModal class="blah"> |
@titleAriaElementId |
Usa la sintassi con parentesi angolari con un attributo HTML regolare: <DModal aria-labelledby="blah"> |
@dismissable, @submitOnEnter, @headerClass |
Invariato |
Se c’era del contenuto nel footer renderizzato dopo il vecchio componente <DModalBody>, usa il nuovo blocco denominato :footer per inserirlo all’interno di <DModal>. Quando si utilizzano blocchi denominati, il contenuto del corpo deve essere avvolto in <:body></:body>. Ad esempio:
<DModal @closeModal={{@closeModal}}>
<:body>
Ciao mondo, questo è il contenuto del modal
</:body>
<:footer>
Questo è il contenuto del footer. Un wrapper `.modal-footer` verrà aggiunto
automaticamente
</:footer>
</DModal>
Passo 4: Aggiorna i punti di chiamata di showModal
In precedenza, i modali venivano renderizzati utilizzando l’API showModal, che richiedeva una stringa (il nome del controller) e diversi parametri opzionali. Restituiva 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 utilizzando qualcosa come getOwner(this).lookup("service:modal")) e chiamare la funzione show().
show() accetta un riferimento alla nuova classe del componente come primo argomento. L’unica opzione ancora supportata è ‘model’, che può essere utilizzata per passare tutti i dati/azioni richiesti dal tuo Modal.
Non verrà restituita alcuna riferimento all’istanza del componente. Invece, show() restituisce una promise che si risolve quando il modal viene chiuso. La promise si risolverà con i dati passati 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/a per i componenti - rimuovilo |
templateName |
n/a per i componenti - rimuovilo |
title |
sposta a <DModal @title={{i18n "blah"}}> |
titleTranslated |
sposta a <DModal @title="blah">. Questo potrebbe essere calcolato in base ai dati di model se necessario |
modalClass |
sposta a <DModal class="blah"> |
titleAriaElementId |
sposta a <DModal aria-labelledby="blah"> |
panels |
Usa il blocco denominato :headerBelowTitle per implementare le schede nel tuo componente (esempio) |
model |
invariato |
Passo 5: Test
Qualsiasi test dovrebbe rimanere sostanzialmente invariato. I problemi più comuni sono:
-
I modali non hanno più una classe predefinita basata sul loro nome. Le classi devono essere specificate esplicitamente nel template (vedi l’inizio del Passo 3)
-
Il wrapper
d-modalnon persiste più nel DOM quando il modal viene chiuso. Per verificare che tutti i modali siano chiusi, usa un controllo comeassert.dom('.d-modal').doesNotExist()
Profitto!
Il tuo modal dovrebbe ora funzionare come prima. Per sfruttare ulteriormente la nuova API, potresti voler considerare 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 modali del core di Discourse alla nuova API:
{small}Questo documento è sottoposto a controllo di versione - suggerisci modifiche su github.{/small}
I Classic Ember Components sono raccomandati in questa guida perché offrono il percorso di migrazione più semplice dai Controller Ember. Tuttavia, per modali semplici, o se sei disposto a dedicare del tempo alla refattorizzazione, i moderni componenti Glimmer sono la scelta migliore. ↩︎