Decidí crear el plugin. El componente del formulario se está renderizando correctamente a través de api.renderInOutlet. Sin embargo, los datos no se guardan en el backend en la tabla personalizada.
Configuración del Frontend:
Estoy usando api.renderInOutlet("composer-fields", MyOutletConnectorComponent) para renderizar un componente GJS (MyOutletConnectorComponent) cuando se está creando un nuevo tema en una categoría designada.
Este MyOutletConnectorComponent renderiza mi componente de formulario GJS principal (MyMarketFormComponenent).
Dentro de MyMarketFormComponenent, la entrada del usuario actualiza un objeto JavaScript que luego se establece como una propiedad de nivel superior en el composerModel proporcionado por los argumentos del outlet. Por ejemplo:
// Dentro de la acción de MyMarketFormComponenent
const currentData = this.args.composerModel.get("market_listing_data") || {};
const updatedData = { ...currentData, [fieldKey]: newValue };
this.args.composerModel.set("market_listing_data", updatedData);
En mi inicializador de plugin (tecenc-market-composer.js), estoy usando api.serializeOnCreate("market_listing_data", "market_listing_data"); para intentar pasar estos datos al backend. También uso api.serializeToDraft("market_listing_data", "market_listing_data");.
Problema del Backend:
En mi plugin.rb, dentro del manejador DiscourseEvent.on(:topic_created) do |topic, opts, user| ... end:
Verifico si el tema está en la categoría correcta (esto funciona).
Luego intento acceder a los datos serializados usando market_data = opts[:market_listing_data] (o opts["market_listing_data"]).
El problema es que opts[:market_listing_data] es consistentemente nil.
Registrar opts.keys confirma que mi clave market_listing_data no está presente en el nivel superior del hash opts. El hash opts en sí contiene claves estándar como :raw, :title, :category, :archetype, etc.
Mi fragmento de plugin.rb para el evento:
# plugin.rb
# ...
module ::TecencMarket
SERIALIZED_DATA_KEY = "market_listing_data".freeze
end
# ...
after_initialize do
# ...
DiscourseEvent.on(:topic_created) do |topic, opts, user|
# ... (lógica de verificación de categoría) ...
# Aquí es donde market_data es nil
market_data = opts[TecencMarket::SERIALIZED_DATA_KEY.to_sym]
Rails.logger.info "[MyPlugin] Opts keys: #{opts.keys.inspect}"
Rails.logger.info "[MyPlugin] market_data from opts: #{market_data.inspect}"
if market_data.present? && market_data.is_a?(Hash)
# ... procesar y guardar datos en tabla personalizada ...
else
Rails.logger.warn "[MyPlugin] Market data not found in opts as expected."
end
end
# ...
end
Pregunta:
¿Cuál es la forma correcta y más confiable de asegurar que un objeto JavaScript establecido como una propiedad de nivel superior en el composerModel (por ejemplo, composerModel.set("my_plugin_data_key", { ... });) usando componentes GJS se serialice correctamente con api.serializeOnCreate y esté disponible en el hash opts del evento :topic_created en el backend?
¿Se espera que api.serializeOnCreate("my_key", "my_key") funcione para claves arbitrarias de nivel superior establecidas en el composerModel desde un contexto GJS, o la serialización a través de custom_fields (por ejemplo, estableciendo composerModel.custom_fields.my_plugin_key = {...} y usando api.serializeOnCreate("custom_fields.my_plugin_key")) es el único enfoque robusto/recomendado para este tipo de transferencia de datos para nuevos temas?
¿Existen consideraciones específicas o errores comunes al usar api.serializeOnCreate con datos administrados por componentes GJS que podrían hacer que los datos no se incluyan en el hash opts?
He confirmado que el formulario del frontend está recopilando datos correctamente y estableciéndolos en la propiedad composerModel.market_listing_data. El problema parece estar puramente en la serialización de estos datos y su paso al evento del backend.
¡Cualquier guía o ejemplo del patrón recomendado para esto en Discourse moderno sería muy apreciado!
Siguiendo con mi problema anterior sobre la falta de datos llegando al hash opts, he cambiado mi estrategia para usar la vía recomendada de custom_fields para serializar datos desde el formulario del compositor en mi plugin.
Enfoque actual:
Inicializador del frontend:
Estoy intentando asegurarme de que composerModel.custom_fields y composerModel.custom_fields["tecenc_market_data"] (la clave de datos de mi plugin) estén inicializados como objetos. Mi último intento consiste en usar api.modifyClass("model:composer", ...) para agregar lógica de inicialización en init() y clearState():
// En mi inicializador de plugin (tecenc-market-composer.js)
api.modifyClass("model:composer", {
pluginId: "tecencMarketInitCustomFieldsOnComposer",
init() {
this._super(...arguments);
if (!this.custom_fields || typeof this.custom_fields !== 'object') {
this.custom_fields = {};
}
if (!this.custom_fields["tecenc_market_data"] || typeof this.custom_fields["tecenc_market_data"] !== 'object') {
this.custom_fields["tecenc_market_data"] = {};
}
},
clearState() {
this._super(...arguments);
if (!this.custom_fields || typeof this.custom_fields !== 'object') {
this.set("custom_fields", {});
}
let cf = this.get("custom_fields");
if (!cf["tecenc_market_data"] || typeof cf["tecenc_market_data"] !== 'object') {
const newCustomFields = { ...cf };
newCustomFields["tecenc_market_data"] = {};
this.set("custom_fields", newCustomFields);
}
}
});
Luego utilizo api.serializeOnCreate('custom_fields.tecenc_market_data'); y api.serializeToDraft('custom_fields.tecenc_market_data');.
Mi componente de formulario GJS actualiza composerModel.custom_fields.tecenc_market_data.my_field = value; (estableciendo un nuevo objeto custom_fields para mantener la reactividad).
Backend plugin.rb:
Se llama a Topic.register_custom_field_type("tecenc_market_data", :json).
El gestor de eventos topic_created espera leer datos de topic.custom_fields["tecenc_market_data"].
Nuevo error crítico:
A pesar de estos intentos de inicializar custom_fields en composerModel, ahora encuentro consistentemente el siguiente error de JavaScript al abrir el compositor para un tema nuevo (al hacer clic en el botón “Crear tema”):
Uncaught (in promise) Error: Property set failed: object in path "custom_fields" could not be found.
Ember 3
open composer.js:1010
open composer.js:1009
_setModel composer.js:1451
open composer.js:1401
openNewTopic composer.js:1410
createTopic list.js:167
clickCreateTopicButton d-navigation.gjs:224
_triggerAction d-button.gjs:138
Ember 11
_triggerAction d-button.gjs:135
click d-button.gjs:93
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:94366
Ember 2
source chunk.8d6366d70b85d1b69fdc.d41d8cd9.js:94040
Ember 26 property_set-BapAkp3X.js:79:10
Este error indica que this.model.custom_fields (en el composerModel) es undefined o null cuando el código principal de Discourse en composer.js:1010 (dentro del método open, llamado por openNewTopic) intenta establecer una propiedad anidada en ello. Esto sucede antes de que el formulario de mi plugin incluso pueda ser renderizado.
Preguntas:
Dado que el modelo principal Composer debería inicializar custom_fields: {}, ¿por qué composerModel.custom_fields aún puede ser indefinido en composer.js:1010 durante la secuencia openNewTopic en esta versión de Discourse?
¿Es el enfoque api.modifyClass("model:composer", ...) en un inicializador la manera correcta o más efectiva para garantizar que custom_fields (y las claves anidadas del plugin dentro de ella) se inicialicen como objetos lo suficientemente temprano para evitar este error principal? Si no, ¿cuál es el patrón recomendado?
¿Podría haber un problema de sincronización en el cual pasos de inicialización del constructor principal de composer se ejecutan y esperan que custom_fields esté listo antes de que los inicializadores del plugin usando api.modifyClass hayan tenido pleno efecto en el prototipo o instancias del modelo Composer?
Cualquier insight sobre este error de “Falló la configuración de la propiedad” en el núcleo del composer y las mejores prácticas para que los plugins interactúen de forma segura con composerModel.custom_fields durante la inicialización sería muy útil.
Estoy guardando los datos en una tabla de base de datos personalizada.
addToSerializer es para mover datos del servidor al cliente, ¿verdad?
Mi problema es mover datos del cliente al servidor para poder guardarlos. Así que estoy usando serializeOnCreate. Pero el problema es que los datos no fluyen al hash opts. Por lo tanto, actualmente, la respuesta es no, los datos personalizados no están llegando al hash opts como se esperaba. No están llegando a Rails.
Investigué este plugin educativo pero no pude hacerlo funcionar para 6 campos personalizados.