Benutzerspezifische Daten vom GJS Composer-Formular nicht in `opts` für das `:topic_created`-Ereignis

Dies ist eine Fortsetzung von: Custom topic fields per category or custom topic entry form per category?

Ich habe beschlossen, das Plugin zu erstellen. Die Formularkomponente selbst wird korrekt über api.renderInOutlet gerendert. Die Daten werden jedoch nicht im Backend in der benutzerdefinierten Tabelle gespeichert.

Frontend-Setup:

  1. Ich verwende api.renderInOutlet("composer-fields", MyOutletConnectorComponent), um eine GJS-Komponente (MyOutletConnectorComponent) zu rendern, wenn ein neues Thema in einer bestimmten Kategorie erstellt wird.

  2. Diese MyOutletConnectorComponent rendert dann meine Haupt-GJS-Formular-Komponente (MyMarketFormComponenent).

  3. Innerhalb von MyMarketFormComponenent aktualisiert die Benutzereingabe ein JavaScript-Objekt, das dann als Top-Level-Eigenschaft auf dem composerModel gesetzt wird, das von den Outlet-Argumenten bereitgestellt wird. Zum Beispiel:

    // Innerhalb der MyMarketFormComponenent-Aktion
    const currentData = this.args.composerModel.get("market_listing_data") || {};
    const updatedData = { ...currentData, [fieldKey]: newValue };
    this.args.composerModel.set("market_listing_data", updatedData);
    
  4. In meinem Plugin-Initialisierer (tecenc-market-composer.js) verwende ich api.serializeOnCreate("market_listing_data", "market_listing_data");, um zu versuchen, diese Daten an das Backend zu übergeben. Ich verwende auch api.serializeToDraft("market_listing_data", "market_listing_data");.

Backend-Problem:

In meinem plugin.rb innerhalb des DiscourseEvent.on(:topic_created) do |topic, opts, user| ... end Handlers:

  • Ich prüfe, ob das Thema zur richtigen Kategorie gehört (das funktioniert).
  • Ich versuche dann, auf die serialisierten Daten zuzugreifen, indem ich market_data = opts[:market_listing_data] (oder opts["market_listing_data"]) verwende.
  • Das Problem ist, dass opts[:market_listing_data] durchweg nil ist.
  • Das Protokollieren von opts.keys bestätigt, dass mein Schlüssel market_listing_data nicht auf der obersten Ebene des opts-Hashs vorhanden ist. Der opts-Hash selbst enthält Standard-Schlüssel wie :raw, :title, :category, :archetype usw.

Mein plugin.rb-Snippet für das Ereignis:

# plugin.rb
# ...
module ::TecencMarket
  SERIALIZED_DATA_KEY = "market_listing_data".freeze
end
# ...
after_initialize do
  # ...
  DiscourseEvent.on(:topic_created) do |topic, opts, user|
    # ... (Logik zur Kategorienprüfung) ...

    # Hier ist market_data 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)
      # ... Daten verarbeiten und in benutzerdefinierter Tabelle speichern ...
    else
      Rails.logger.warn "[MyPlugin] Market data not found in opts as expected."
    end
  end
  # ...
end

Frage:

  1. Was ist der richtige und zuverlässigste Weg, um sicherzustellen, dass ein JavaScript-Objekt, das als Top-Level-Eigenschaft auf dem composerModel gesetzt wird (z. B. composerModel.set("my_plugin_data_key", { ... });) mithilfe von GJS-Komponenten, korrekt von api.serializeOnCreate serialisiert und im opts-Hash des :topic_created-Ereignisses im Backend verfügbar gemacht wird?

  2. Ist api.serializeOnCreate("my_key", "my_key") für beliebige Top-Level-Schlüssel auf dem composerModel aus einem GJS-Kontext erwartet, oder ist die Serialisierung über custom_fields (z. B. das Setzen von composerModel.custom_fields.my_plugin_key = {...} und die Verwendung von api.serializeOnCreate("custom_fields.my_plugin_key")) der einzige robuste/empfohlene Ansatz für diese Art der Datenübertragung für neue Themen?

  3. Gibt es spezielle Überlegungen oder häufige Fallstricke bei der Verwendung von api.serializeOnCreate mit von GJS-Komponenten verwalteten Daten, die dazu führen könnten, dass die Daten nicht im opts-Hash enthalten sind?

Ich habe bestätigt, dass das Frontend-Formular Daten korrekt sammelt und sie auf der Eigenschaft composerModel.market_listing_data setzt. Das Problem scheint rein darin zu liegen, diese Daten zu serialisieren und an das Backend-Ereignis zu übergeben.

Jede Anleitung oder Beispiele für das empfohlene Muster hierfür im modernen Discourse wären sehr willkommen!

Danke!

Hallo nochmal,

Zurückverfolgung meines vorherigen Problems, dass Daten nicht im opts-Hash ankommen, habe ich meine Strategie geändert und verwende nun den empfohlenen Weg über custom_fields, um Daten aus dem Composer-Formular meines Plugins zu serialisieren.

Aktueller Ansatz:

  1. Frontend-Initializer:
    • Ich versuche sicherzustellen, dass composerModel.custom_fields und composerModel.custom_fields["tecenc_market_data"] (mein Daten-Schlüssel im Plugin) als Objekte initialisiert sind. Mein letzter Versuch beinhaltet die Verwendung von api.modifyClass("model:composer", ...), um die Initialisierungslogik zu init() und clearState() hinzuzufügen:

      // In meinem Plugin-Initializer (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);
          }
        }
      });
      
    • Ich verwende dann api.serializeOnCreate('custom_fields.tecenc_market_data'); und api.serializeToDraft('custom_fields.tecenc_market_data');.

    • Mein GJS-Formular-Komponent aktualisiert composerModel.custom_fields.tecenc_market_data.my_field = wert; (indem es ein neues custom_fields-Objekt für die Reaktivität setzt).

  2. Backend plugin.rb:
    • Topic.register_custom_field_type("tecenc_market_data", :json) wird aufgerufen.
    • Der Event-Handler topic_created liest Daten aus topic.custom_fields["tecenc_market_data"].

Neuer kritischer Fehler:

Trotz dieser Versuche, custom_fields im composerModel zu initialisieren, erhalte ich nun konstant den folgenden JavaScript-Fehler, wenn ich den Composer für ein neues Thema öffne (durch Klicken auf den Button „Thema erstellen“):


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

Diese Fehleranzeige deutet darauf hin, dass this.model.custom_fields (im composerModel) undefined oder null ist, wenn der Kerncode von Discourse bei composer.js:1010 (innerhalb der open-Methode, die von openNewTopic aufgerufen wird) versucht, eine verschachtelte Eigenschaft zu setzen. Dies geschieht bevor mein Plugin-Formular überhaupt gerendert werden kann.

Fragen:

  1. Angesichts dessen, dass das Kernmodell Composer custom_fields: {} initialisieren sollte, warum könnte composerModel.custom_fields zum Zeitpunkt composer.js:1010 während der openNewTopic-Sequenz in dieser Discourse-Version trotzdem undefined sein?
  2. Ist der Ansatz `api.modifyClass(

Ich denke, du solltest deine Daten auf der Rails-Seite zum Serializer hinzufügen.

Kommt deine Daten bei Rails an? Wo speicherst du sie?

Sieh dir Plugins an, die addToSerializer haben.

Ich speichere die Daten in einer benutzerdefinierten Datenbanktabelle.

addToSerializer dient dazu, Daten vom Server zum Client zu verschieben, nicht wahr?

Mein Problem ist, Daten vom Client zum Server zu verschieben, damit ich sie speichern kann. Deshalb verwende ich serializeOnCreate. Aber das Problem ist, dass die Daten nicht im opts-Hash ankommen. Die Antwort lautet also derzeit: Nein, die benutzerdefinierten Daten gelangen nicht wie erwartet in den opts-Hash. Sie gelangen nicht nach Rails.

Ich habe mir dieses Schulungsplugin angesehen, konnte es aber nicht für 6 benutzerdefinierte Felder zum Laufen bringen.

1 „Gefällt mir“

Es funktioniert jetzt.

Ich habe diesen Schritt vermisst: api.serializeToTopic(MY_BLOB_KEY, \topic.${MY_BLOB_KEY}\);

2 „Gefällt mir“