Si vous visitez /tags/category-slug/tag-name et cliquez sur le bouton \u003ckbd\u003eNouveau sujet\u003c/kbd\u003e, le compositeur a déjà le tag préconfiguré, comme décrit ici :
C’est génial. Mais maintenant, je (et au moins une autre personne) souhaitons pouvoir définir ce comportement avec un tag par défaut lors de la visite de /c/cat-slug/cat-id. Il semble qu’un composant de thème devrait pouvoir cibler ce bouton pour le modifier, ou le masquer et ajouter un nouveau bouton (il y a une prise de plugin juste là que je ne parviens pas à retrouver pour l’instant, mais que j’ai vue il y a une minute).
Est-ce censé fonctionner uniquement sur une catégorie particulière, ou auriez-vous besoin qu’il prenne en charge un « tag par défaut » pour de nombreuses catégories, où ce tag serait différent pour chacune d’elles ?
J’imagine que je pourrais configurer un tag par défaut pour quelques catégories. Je devrais pouvoir le faire, mais je ne sais pas où ni comment modifier le bouton « Créer un sujet » pour qu’il inclue ce tag par défaut.
Les identifiants sur les éléments HTML doivent être uniques ; en d’autres termes, aucun deux éléments dans la même vue ne peuvent partager le même attribut d’identifiant HTML. Cela nous suffit donc pour commencer.
Si je recherche "create-topic" sur GitHub, voici ce que je vois…
Il n’y a qu’un seul résultat, nous avons donc de la chance. S’il y avait plus de résultats, il existerait des méthodes pour affiner la liste, mais cela sort du cadre de ce sujet.
Examinons donc ce fichier.
Vous verrez alors que l’action associée au bouton est définie ainsi :
action=action
Eh bien… ce n’est pas très utile… Alors, que faire maintenant ?
Lorsque vous voyez action=action, cela signifie que l’action est transmise au composant depuis un modèle parent.
Essayons de voir quels modèles utilisent ce composant. Nous allons donc sur GitHub et recherchons le nom du composant tel qu’il serait utilisé dans un modèle. Pour cet exemple, nous utiliserions quelque chose comme ceci : "{{create-topic-button"
Notez que j’ai seulement ajouté {{NOM_DU_COMPOSANT et ignoré le reste. Nous ne connaissons pas les autres arguments qui lui sont passés, nous voulons donc une recherche générique.
Nous obtenons deux résultats… l’un d’eux se trouve dans le plugin styleguide, nous l’ignorons donc simplement. L’autre se trouve dans le noyau (core). Voyons donc à quoi cela ressemble :
Ahh… nous nous rapprochons. Vous voyez maintenant que l’action pour le bouton est :
action=(action "clickCreateTopicButton")
Nous devons maintenant découvrir ce que fait cette action. Nous recherchons donc le nom de l’action. Ensuite, nous filtrons pour ne garder que les fichiers .js, car nous voulons maintenant voir la définition de cette action dans le fichier JS du composant.
Encore une fois, nous n’obtenons qu’un seul résultat, alors examinons-le.
Il semble donc que l’action fasse l’une des deux choses suivantes. Si la catégorie est en lecture seule et que l’utilisateur n’a pas déjà de brouillon, elle affiche une alerte. Sinon, elle appelle une méthode createTopic().
Nous nous intéressons à cette dernière, alors examinons-la.
Si vous recherchez createTopic() dans ce fichier (recherche interne, pas sur GitHub)… vous remarquerez qu’il n’y a qu’une seule référence. Que se passe-t-il ? Comment ce composant appelle-t-il une méthode qui n’est pas définie ?
Eh bien, la réponse se trouve plus haut dans le fichier.
Que signifie cela ?
Je ne veux pas perdre trop de temps ici, mais Ember utilise des Classes. Pensez aux classes comme à des bundles de code réutilisables. Toute la ligne surlignée ci-dessus signifie simplement :
Prenez le bundle Component d’Ember, ajoutez-y le bundle FilterModeMixin et permettez-moi d’ajouter d’autres méthodes, ou d’en remplacer certaines existantes, au résultat pour créer un nouveau composant Ember pour mon application.
Revenons donc à l’action que nous essayons de tracer.
Elle appelle this.createTopic(). Ce n’est pas une méthode par défaut du composant Ember. C’est une méthode personnalisée de Discourse, elle doit donc provenir de FilterModeMixin. Qu’est-ce que FilterModeMixin ? Eh bien… il est défini au début du fichier.
import FilterModeMixin from "discourse/mixins/filter-mode";
Faites une pause une seconde et effectuez une recherche interne pour createTopic() dans ce fichier. Je le dis vraiment. Arrêtez de lire et faites-le. J’attends… ne trichez pas… j’ai les sur vous.
OK. Vous avez cherché, et il n’y a aucun résultat. Que faire maintenant ?
Ce que j’ai décrit ci-dessus n’est qu’une méthode pour transmettre des éléments vers le bas. Si vous ne trouvez pas ce que vous cherchez, reculez d’un pas et essayez une approche différente.
Récapitulons donc… où en sommes-nous ? Avant de rester bloqués, nous examinions le fichier JS du composant d-navigation. Regardons son modèle.
Encore une fois, nous utilisons "{{NOM_DU_COMPOSANT" et recherchons.
Cela a-t-il de l’importance ? Peut-être. Cela a-t-il de l’importance dans ce cas ? Non. Nous essayons simplement de comprendre d’où vient createTopic() ou ce que c’est. Continuons donc avec le premier résultat.
Super… encore du jargon… parce que tout le monde adore ça
Sérieusement, parlons des actions de route. Qu’est-ce que c’est ? Eh bien, ce sont des actions de route… des actions ? Comme des actions définies sur la route. Pourquoi sont-elles pratiques ? Parce que les routes dans Discourse peuvent être imbriquées.
Voyez cela ainsi :
- route-1
- route-1-1
- route-1-2
- route-1-3
Si j’ai un composant partagé que je dois utiliser sur les routes 111, 112 et 113 avec des paramètres différents, ne serait-il pas plus simple de définir le même composant pour toutes et de passer la même action ? Ensuite, je pourrais le modifier pour chaque route si nécessaire ?
C’est ce que font les route-actions.
OK, revenons à la question. Nous examinions :
createTopic=(route-action "createTopic")
dans le composant navigation/default.
Il ne nous reste plus qu’à déterminer quelle est la route pour vérifier ce que fait cette action de route.
Vous voulez modifier le comportement du bouton « Nouveau sujet » sur les pages /c/cat-slug/cat-id. Visitons donc l’une de ces pages. Par exemple : http://localhost:4200/c/meta/6
Quelle est cette route ? À moins d’être très familier avec Discourse, vous ne pourriez pas le dire. Que faire maintenant ?
C’est là que l’extension Ember pour votre navigateur devient utile.
Installez-la ici si vous ne l’avez pas déjà. J’attends.
(le lien est un dépôt GitHub, mais la description contient les liens vers l’extension pour différents navigateurs)
OK, maintenant que vous l’avez installée, retournez sur cette page /c/cat-slug/cat-id et regardez la page de l’extension.
Une fois chargée, cliquez sur Routes, puis activez « Current Route only ».
Ahhh… regardez ça. Nous savons maintenant sur quelle route nous nous trouvons. Nous sommes sur discovery.category.
Mais ce n’est pas toute l’histoire… c’est :
application > discovery > discovery.category
Rappelez-vous, les routes sont imbriquées. Que faire maintenant ?
Je commence généralement tout en haut. Dans ce cas, ce serait la route application. Trouvez le fichier de cette route et recherchez pour voir si l’action y est définie.
Il semble qu’elle fasse l’une des deux choses suivantes. Si l’utilisateur a un brouillon, elle l’ouvre. Sinon, elle appelle openComposer() avec un paramètre. Quelle est la suite ? Eh bien, vous devriez déjà connaître la réponse. Nous devons découvrir d’où vient openComposer() ou ce qu’il fait.
Nous recherchons donc openComposer() dans le fichier et… bien sûr, nous n’obtenons aucun résultat. Il n’y a pas de méthode nommée openComposer() dans cette route.
Que faire ensuite ? Rappelez-vous le passage sur les Classes Ember ? Essayons cela.
Nous avons ceci au début du fichier de route.
Cela signifie que cette route hérite de toutes les méthodes du bundle DiscourseRoute ainsi que de celles définies dans le bundle OpenComposer.
openComposer est plus susceptible d’être ce que nous voulons, alors examinons cela. Mais avant cela… nous devons voir comment openComposer est défini dans ce fichier.
import OpenComposer from "discourse/mixins/open-composer";
Regardez l’URL. Ce n’est pas un composant Ember. Ce n’est pas une route, ni un modèle. C’est un mixin. Qu’est-ce qu’un mixin ? La réponse très, très courte… c’est un bundle de fonctions réutilisables.
Vous définissez cela dans votre mixin.
add(number) {
return number + 1
}
substract(number) {
return number - 1
}
ensuite, vous ajoutez le mixin à votre composant Ember, puis vous pouvez faire quelque chose comme ceci :
// valeur de départ est 1
myMethod () {
this.add(value) // retourne 2
this.substract(value) // retourne 0
}
Alors, comment cela se rapporte-t-il à ce que nous essayons de faire ?
Eh bien, open-composer ici.
import OpenComposer from "discourse/mixins/open-composer";
est un mixin. L’une des méthodes de ce mixin est OpenComposer().
Ce n’est pas grave si vous vous sentez confus. Ils partagent le même nom - sauf que l’un commence par une majuscule, ce qui indique qu’il s’agit d’une Classe.
Ils signifient des choses différentes.
Pour comprendre cela, vous devriez savoir que le nom que vous donnez à vos modules importés n’a pas d’importance (dans ce cas particulier), tant qu’ils sont exportés comme « default ».
Expliquer cela dépasse un peu le cadre de ce sujet. Tout ce que vous devez savoir, c’est que ceci :
ID HTML du bouton Nouveau sujet < Action du bouton Nouveau sujet < Action du composant d-navigation < Action de la route discovery < Mixin OpenComposer < Méthode openComposer()
Donc… c’est la méthode qui est finalement appelée lorsque vous cliquez sur le bouton + Nouveau sujet sur cette route.
Nous avons établi comment déterminer l’action de ce bouton sur /c/cat-slug/cat-id, mais cela semble différent de ce qui se passe lorsque vous visitez /tags/category-slug/tag-name, ce que vous souhaitez faire.
Quelle est donc la prochaine étape ? Examinons ce que fait cette route pour gérer l’action createTopic().
Cette différence correspond à peu près à ce que vous demandez ici.
Donc, tout ce que vous avez à faire est… de modifier l’action createTopic() dans la route discovery pour qu’elle fonctionne comme sur la route tag-show. Comment faire cela ?
Rappelez-vous comment nous avons parlé d’Ember utilisant des Classes ? Oui, nous allons devoir y revenir.
L’API des plugins vous permet de modifier les classes Ember via cette méthode.
Alors, que tentons-nous de modifier ici ? La route discovery… car… rappelez-vous, c’est là que createTopic() est défini lorsque vous êtes sur une page comme /c/cat-slug/cat-id.
Que fait cela ? Cela brise le bouton + Nouveau sujet ; cependant, cela nous indique que nous sommes dans la bonne direction. Si vous essayez d’ajouter l’extrait ci-dessus, vous remarquerez que cliquer sur le bouton n’ouvre plus le compositeur. Au lieu de cela, il affiche simplement un message dans la console. C’est une bonne chose car cela signifie que nous avons ciblé la bonne Classe et la bonne action - route:discovery et createTopic().
Que faire ensuite ? Eh bien, rappelez-vous que le bouton sur /tags/category-slug/tag-name fait exactement ce que nous voulons. Copions donc le code de cette route et ajoutons les imports nécessaires.
Cela fonctionnera-t-il ? Non, mais nous sommes à un pas de la solution. Pourquoi cela ne fonctionne-t-il pas ? Parce que les tags qu’il ajoute lorsque le compositeur s’ouvre ne sont pas définis. Pourquoi ? Parce qu’ils sont chargés depuis le contrôleur tag.show - ce que nous ne voulons pas. Modifions le code pour qu’il fonctionne avec la route sur laquelle nous nous trouvons.
Avant de le faire, cependant, nous avons besoin d’un certain index pour nos tags par défaut souhaités. Utilisons un nouvel objet comme ceci :
// slug-de-categorie: [TABLEAU_TAGS_PAR_DEFAUT]
const defaultTagIndex = {
// slug d'un seul mot
meta: ["a", "b", "c"],
core: ["g", "h"],
// slug avec un tiret
["general-chat"]: ["d", "e", "f"]
};
Cela signifie essentiellement que si le compositeur est ouvert sur la page de la catégorie meta, ajoutez les tags « a, b, c ».
Si le compositeur est ouvert sur la page de la catégorie core, ajoutez les tags « g, h », etc.
Maintenant que nous avons cela, nous pouvons modifier l’action pour qu’elle ressemble à ceci.
J’ai enveloppé tout le code dans un bloc try…catch. Si le code échoue, nous exécutons this._super(...arguments).
Si vous êtes familier avec Ember, vous savez ce que fait this._super(...arguments). Sinon, voici une explication simple. Nous remplaçons createTopic(), donc si les remplacements échouent en raison d’une erreur - peut-être que le noyau a été mis à jour - alors nous revenons à la méthode du noyau telle que définie ici.
Si l’utilisateur a un brouillon de nouveau sujet, nous revenons simplement à this._super(...arguments) et laissons le noyau faire son travail.
Cela devrait suffire. Tout ce que vous avez à ajouter maintenant est un moyen de créer l’index des tags par défaut via les paramètres du thème.