Si vous implémentez un nouveau Modal, consultez la documentation principale ici. Ce sujet décrit comment migrer un Modal basé sur un contrôleur existant vers la nouvelle API basée sur des composants.
Par le passé, Discourse utilisait une API basée sur les contrôleurs Ember pour afficher les modales. Pour invoquer la modale, vous passiez une chaîne de caractères contenant le nom du contrôleur à showModal(). En interne, cela utilisait l’API Route#renderTemplate d’Ember, qui est dépréciée dans Ember 3.x et sera supprimée dans Ember 4.x.
Pour permettre à Discourse de passer à Ember 4.x et aux versions ultérieures, nous avons introduit une nouvelle API basée sur des composants pour les modales. Cette nouvelle API adopte les modèles de conception « déclaratifs » d’Ember et vise à fournir des sémantiques propres DDAU (Data Down, Actions Up).
Étape 1 : Déplacer les fichiers
Déplacez le fichier JS du contrôleur et le fichier de modèle dans le répertoire /components/modal. Cela en fait un « composant co-localisé » qui peut être importé comme n’importe quel autre module JS.
Étape 2 : Mettre à jour le fichier JS
Ensuite, mettez à jour la définition du composant JS pour qu’il hérite de @ember/component au lieu de @ember/controller [1]. Supprimez le mixin ModalFunctionality et mettez à jour les utilisations de ses fonctions selon le tableau ci-dessous :
| Avant | Après |
|---|---|
flash() et clearFlash() |
Créez une propriété flash dans votre composant et passez-la à l’argument @flash de <DModal>. Par défaut, l’alerte sera stylisée avec la classe alert qui est une copie de la classe ‘error’, mais cela peut être remplacé en utilisant l’argument @flashType. |
showModal() |
Importez la fonction showModal depuis discourse/lib/show-modal |
action closeModal |
Invoquez l’argument closeModal qui est automatiquement passé à votre composant |
Les contrôleurs de modales de l’ancien style vivaient « pour toujours », ce qui signifiait que nous devions nettoyer manuellement l’état. Avec la nouvelle API basée sur des composants, le composant sera créé et détruit lorsque la modale est affichée/masquée. Dans de nombreux cas, cela signifie que vos anciens hooks de cycle de vie ne sont plus nécessaires.
Si vous avez toujours besoin de logique basée sur le cycle de vie, utilisez ce tableau :
| Avant | Après |
|---|---|
onShow() |
Utilisez le cycle de vie standard des composants Ember (init() ou modificateur Ember) |
afterRender |
Utilisez le cycle de vie standard des composants Ember (init() ou modificateur Ember) |
beforeClose() |
Créez un wrapper autour de l’argument @closeModal qui est passé à votre composant. Passez une référence à votre wrapper de fermeture à DModal comme <DModal @closeModal={{this.myCloseModalWrapper}}> |
onClose() |
Utilisez le cycle de vie standard des composants Ember (willDestroy() ou modificateur Ember) |
Étape 3 : Mettre à jour le modèle (Template)
Remplacez l’enveloppe <DModalBody> par <DModal>. Ajoutez quelques nouveaux attributs :
- Transmettez l’argument
@closeModalnouvellement introduit - Ajoutez une classe explicite. Pour correspondre au comportement précédent, prenez le nom du fichier de votre contrôleur et ajoutez-y
-modal.
Par exemple, si votre contrôleur de modale s’appelait close-topic.js, l’appel du nouveau <DModal> ressemblerait à ceci :
<DModal @closeModal={{@closeModal}} class="close-topic-modal">
Si l’appel DModalBody contient d’autres arguments, mettez-les à jour en fonction du tableau ci-dessous :
| Avant | Après |
|---|---|
@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 |
Utilisez la syntaxe à chevrons avec un attribut html standard : <DModal class="blah"> |
@titleAriaElementId |
Utilisez la syntaxe à chevrons avec un attribut html standard : <DModal aria-labelledby="blah"> |
@dismissable, @submitOnEnter, @headerClass |
Inchangé |
S’il y avait du contenu de pied de page rendu après l’ancien composant <DModalBody>, utilisez le nouveau bloc nommé <:footer> pour l’introduire à l’intérieur de <DModal>. Lorsque vous utilisez des blocs nommés, le contenu du corps doit être enveloppé dans <:body></:body>. Par exemple :
<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>
Étape 4 : Mettre à jour les appels showModal
Auparavant, les modales étaient rendues à l’aide de l’API showModal, qui prenait une chaîne de caractères (le nom du contrôleur) et un certain nombre d’options. Elle renvoyait une instance du contrôleur qui pouvait être manipulée :
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);
});
}
Pour afficher les nouvelles modales basées sur des composants, vous devez injecter le service « modal » (ou y accéder en utilisant quelque chose comme getOwner(this).lookup("service:modal")) et appeler la fonction show().
show() prend une référence à la nouvelle classe de composant comme premier argument. La seule option encore prise en charge est « model », qui peut être utilisée pour passer toutes les données/actions requises pour votre modale.
Aucune référence à l’instance du composant ne sera renvoyée. Au lieu de cela, show() renvoie une promesse qui sera résolue lorsque la modale sera fermée. La promesse sera résolue avec toutes les données qui ont été passées à @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 },
});
});
}
Alternativement, migrez vers l’API déclarative décrite dans la documentation principale de DModal.
La fonctionnalité des anciennes options peut être reproduite comme suit :
Option showModal ancienne |
Solution |
|---|---|
admin |
n/a pour les composants - supprimez-le |
templateName |
n/a pour les composants - supprimez-le |
title |
déplacez vers <DModal @title={{i18n "blah"}}> |
titleTranslated |
déplacez vers <DModal @title="blah">. Cela peut être calculé en fonction des données de model si nécessaire |
modalClass |
déplacez vers <DModal class="blah"> |
titleAriaElementId |
déplacez vers <DModal aria-labelledby="blah"> |
panels |
Utilisez le bloc nommé <:headerBelowTitle> pour implémenter des onglets dans votre composant (exemple) |
model |
inchangé |
Étape 5 : Tests
Les tests devraient rester largement les mêmes. Le problème le plus courant est :
-
Les modales n’ont plus de classe par défaut basée sur leur nom. Les classes doivent être spécifiées explicitement dans le modèle (voir le début de l’étape 3)
-
L’enveloppe
d-modalne persiste plus dans le DOM lorsque la modale est fermée. Pour vérifier que toutes les modales sont fermées, utilisez une vérification commeassert.dom('.d-modal').doesNotExist()
Récompense !
Votre modale devrait maintenant fonctionner comme avant. Pour tirer davantage parti de la nouvelle API, vous pourriez envisager de remplacer les appels showModal par une stratégie déclarative et de convertir votre modale en composant Glimmer.
Exemples
Voici quelques exemples de commits qui démontrent la conversion de certaines des modales du noyau de Discourse vers la nouvelle API :
Ce document est sous contrôle de version - suggérez des modifications sur github.
Les composants Ember classiques sont recommandés dans ce guide car ils offraient le chemin de migration le plus simple depuis les contrôleurs Ember. Mais pour les modales simples, ou si vous êtes prêt à consacrer du temps à la refactorisation, les composants Glimmer modernes sont le meilleur choix. ↩︎