Si riscontra un errore di rendering persistente durante il tentativo di visualizzare un componente Glimmer (.gjs) come modale utilizzando this.modal.show(). Il modale viene attivato da un altro componente GJS aggiunto al menu del post tramite il trasformatore di valore post-menu-buttons. Sto eseguendo Discourse v3.5.0.beta3-dev.
Sto cercando di aggiungere un pulsante al menu del post utilizzando api.registerValueTransformer("post-menu-buttons", ...). Fare clic su questo pulsante dovrebbe aprire un modale definito da un componente GJS separato (FeedbackFormModal) utilizzando this.modal.show(FeedbackFormModal, ...).
Quando si fa clic sul pulsante e si chiama this.modal.show(), l’applicazione va in crash con il seguente errore, apparentemente durante il processo di rendering di FeedbackFormModal:
Errore verificato:
- Durante il rendering:
-top-level
application
(component solo template sconosciuto)
DiscourseRoot
ModalContainer
FeedbackFormModal
DModal
conditional-in-element:ConditionalInElement
(component solo template sconosciuto) index.js:3970:18
Errore verificato: index.js:3377:16
Uncaught (in promise) Error: Tentativo di utilizzare un valore come helper, ma non era un oggetto o una funzione. Le definizioni degli helper devono essere oggetti o funzioni con un gestore di helper associato. Il valore era: undefined
Ember 2
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:89256
Ember 2
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:90066
Ember 4
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:90164
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:89224
Ember 2
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:90163
Ember 2
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:90284
Ember 2
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:92117
Ember 12
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:94577
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:96288
Ember 34
show modal.js:73
openFeedbackModal leave-feedback-button.js:102 // Il numero di riga potrebbe differire leggermente
_triggerAction d-button.gjs:138
Ember 10
Ciò accade anche quando il template di FeedbackFormModal è ridotto al minimo assoluto, contenendo solo componenti core importati (<DModal>, <DButton>) e helper integrati standard (if, on, ecc.).
Incollo il codice per riferimento:
plugin.rb
# frozen_string_literal: true
# name: my-plugin
module ::MyPlugin
# ... costanti ...
end
require_relative "lib/my_plugin/engine"
after_initialize do
# Carica Dipendenze (utilizzando require_dependency e File.expand_path)
require_dependency File.expand_path('app/controllers/my_plugin/my_controller.rb', __dir__)
require_dependency File.expand_path('app/models/my_plugin/my_model.rb', __dir__)
# ... altre dipendenze ...
# Aggiunge metodi a User (utilizzando class_eval poiché prepend ha fallito)
::User.class_eval do
# Definisce metodi helper come my_custom_stat, ecc.
def my_custom_stat; # ... implementazione ...; end
public :my_custom_stat
# ... altri metodi ...
end
# Prepend Guardian Extensions (se presenti)
# ::Guardian.prepend(MyPlugin::GuardianExtensions)
# Modifiche ai Serializer
reloadable_patch do |plugin|
# Aggiunge attributi ai serializer, ad es.:
add_to_serializer(:post, :some_flag_for_button) do
# Logica per determinare se il pulsante deve essere mostrato
true # Esempio
end
# ... altre aggiunte ai serializer ...
end
end
assets/javascripts/discourse/initializers/my-plugin-outlets.js
import { apiInitializer } from "discourse/lib/api";
import { hbs } from "ember-cli-htmlbars";
import LeaveFeedbackButton from "../components/leave-feedback-button"; // Componente pulsante
// ... importa altri componenti per altri outlet ...
export default apiInitializer("1.13.0", (api) => {
// Usa Value Transformer per il pulsante del menu post
api.registerValueTransformer("post-menu-buttons", ({ value: dag, context }) => {
const { post } = context;
// Logica per determinare se il pulsante deve essere renderizzato in base a post.some_flag_for_button
const shouldRenderButton = post?.some_flag_for_button; // Flag di esempio
if (shouldRenderButton) {
dag.add("leaveMyPluginFeedback", LeaveFeedbackButton, {
after: "like",
args: { post: post },
});
}
return dag;
});
// ... renderInOutlet per altri elementi UI ...
});
Componente Pulsante (assets/javascripts/discourse/components/leave-feedback-button.gjs)
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import FeedbackFormModal from "./feedback-form-modal"; // Il componente modale
export default class LeaveFeedbackButton extends Component {
@service modal;
@service appEvents; // Usato per la visualizzazione degli errori
// Args: post
get buttonLabel() { return "Action Button"; } // Hardcoded
get buttonTitle() { return "Perform Action"; } // Hardcoded
@action
openFeedbackModal() {
console.log("Opening Modal...");
try {
// Modello semplificato per il test
const modelData = { post_id: this.args.post.id };
this.modal.show(FeedbackFormModal, { model: modelData });
} catch(e) {
console.error("Error showing modal", e);
this.appEvents.trigger("show:error", "Error opening modal.");
}
}
<template>
<DButton
class="btn-default my-plugin-btn"
@action={{this.openFeedbackModal}}
@icon="star" {{!-- Icona di esempio --}}
@label={{this.buttonLabel}}
title={{this.buttonTitle}}
/>
</template>
}
Componente Modale (assets/javascripts/discourse/components/feedback-form-modal.gjs - Ultra-Semplificato)
import Component from "@glimmer/component"; // Assicurati che sia importato
// Rimosso tracked, action, service ecc. se non necessari per la versione semplificata
import DModal from "discourse/components/d-modal"; // Assicurati che sia importato
import DButton from "discourse/components/d-button"; // Assicurati che sia importato
import { on, preventDefault } from '@ember/modifier'; // Importa i built-in se usati
export default class FeedbackFormModal extends Component {
// JS minimo necessario per il template semplificato
// Getter di esempio necessario per il template
get modalTitle() { return "My Modal Title"; } // Hardcoded
get cancelLabel() { return "Cancel"; }
get submitLabel() { return "Submit"; }
// Azione fittizia se necessaria dal pulsante
@action submitFeedback() { console.log("Dummy submit"); }
{{!-- TEMPLATE ULTRA-SEMPLIFICATO CHE CAUSA ANCORA ERRORE --}}
<template>
<DModal @title={{this.modalTitle}} @closeModal={{@closeModal}} class="feedback-form-modal">
<:body>
<p>--- TEST MODALE MINIMO ---</p>
{{#if this.errorMessage}} {{!-- Utilizzo dell'helper 'if' integrato --}}
<div class="alert alert-error" role="alert">{{this.errorMessage}}</div>
{{/if}}
</:body>
<:footer>
<DButton @action={{@closeModal}} class="btn-flat"> {{this.cancelLabel}} </DButton>
<DButton @action={{this.submitFeedback}} class="btn-primary" @icon={{if this.isSubmitting "spinner"}}> {{!-- Utilizzo dell'helper 'if' integrato --}}
{{this.submitLabel}}
</DButton>
</:footer>
</DModal>
</template>
}
Dato che l’errore Attempted to use a value as a helper... undefined persiste anche quando si renderizza un template GJS ultra-semplificato (contenente solo componenti core importati come <DModal>/<DButton> e helper integrati come if) tramite this.modal.show() attivato da un componente aggiunto tramite registerValueTransformer("post-menu-buttons", ...), cosa potrebbe causarlo?
Esiste un problema noto o una limitazione con la risoluzione dello scope degli helper/componenti nei modali attivati in questo modo nelle versioni recenti di Discourse (in particolare 3.5.0.beta3-dev)? Esistono pattern alternativi consigliati per mostrare form modali dai pulsanti del menu post in GJS?
Qualsiasi suggerimento sarebbe molto apprezzato!

