נתוני מותאם אישית מ- GJS composer form לא ב-`opts` לאירוע `:topic_created`

This is in continuation to this- Custom topic fields per category or custom topic entry form per category?

I decided to create the plugin. Form component itself is rendering correctly via `api.renderInOutlet`. However the data is not getting saved on the backend in the custom table.

Frontend Setup:

  1. I’m using api.renderInOutlet("composer-fields", MyOutletConnectorComponent) to render a GJS component (MyOutletConnectorComponent) when a new topic is being created in a designated category.

  2. This MyOutletConnectorComponent then renders my main GJS form component (MyMarketFormComponenent).

  3. Inside MyMarketFormComponenent, user input updates a JavaScript object which is then set as a top-level property on the composerModel provided by the outlet arguments. For example:

    // Inside MyMarketFormComponenent action
    const currentData = this.args.composerModel.get("market_listing_data") || {};
    const updatedData = { ...currentData, [fieldKey]: newValue };
    this.args.composerModel.set("market_listing_data", updatedData); 
    
  4. In my plugin initializer (tecenc-market-composer.js), I’m using api.serializeOnCreate("market_listing_data", "market_listing_data"); to try and pass this data to the backend. I also use api.serializeToDraft("market_listing_data", "market_listing_data");.

Backend Problem:

In my plugin.rb, within the DiscourseEvent.on(:topic_created) do |topic, opts, user| ... end handler:

  • I check if the topic is in the correct category (this works).
  • I then try to access the serialized data using market_data = opts[:market_listing_data] (or opts["market_listing_data"]).
  • The issue is that opts[:market_listing_data] is consistently nil.
  • Logging opts.keys confirms that my market_listing_data key is not present at the top level of the opts hash. The opts hash itself contains standard keys like :raw, :title, :category, :archetype, etc.

My plugin.rb snippet for the event:

# plugin.rb
# ...
module ::TecencMarket
  SERIALIZED_DATA_KEY = "market_listing_data".freeze
end
# ...
after_initialize do
  # ...
  DiscourseEvent.on(:topic_created) do |topic, opts, user|
    # ... (category check logic) ...

    # This is where market_data is 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)
      # ... process and save data to custom table ...
    else
      Rails.logger.warn "[MyPlugin] Market data not found in opts as expected."
    end
  end
  # ...
end

Question:

  1. What is the correct and most reliable way to ensure that a JavaScript object set as a top-level property on the composerModel (e.g., composerModel.set("my_plugin_data_key", { ... });) using GJS components is properly serialized by api.serializeOnCreate and made available in the opts hash of the :topic_created event on the backend?

  2. Is api.serializeOnCreate("my_key", "my_key") expected to work for arbitrary top-level keys set on the composerModel from a GJS context, or is serializing via custom_fields (e.g., setting composerModel.custom_fields.my_plugin_key = {...} and using api.serializeOnCreate("custom_fields.my_plugin_key")) the only robust/recommended approach for this kind of data transfer for new topics?

  3. Are there any specific considerations or common pitfalls when using api.serializeOnCreate with data managed by GJS components that might cause the data not to be included in the opts hash?

I’ve confirmed the frontend form is collecting data correctly and setting it on the composerModel.market_listing_data property. The issue seems to be purely in getting this data serialized and passed to the backend event.

Any guidance or examples of the recommended pattern for this in modern Discourse would be greatly appreciated!

Thanks!

שלום שוב,

בנוגע לבעיה הקודמת שלי לגבי כך שהנתונים לא מגיעים למדף opts, שיניתי את האסטרטגיה שלי לשימוש בנתיב המומלץ custom_fieldsסידור לשרשור נתונים מטופס ה-Composer של הפלאגין שלי.

גישה נוכחית:

  1. מאתחל בשרת:
    • אני מנסה להבטיח ש-composerModel.custom_fields ו-composerModel.custom_fields["tecenc_market_data"] (מפתח הנתונים של הפלאגין שלי) מאותחלים כאובייקטים. הניסיון האחרון שלי כולל שימוש ב-api.modifyClass("model:composer", ...) כדי להוסיף לוגיקת אתחול ל-init() ו-clearState():

      // באתחול הפלאגין שלי (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);
          }
        }
      });
      
    • אחר כך אני משתמש ב-api.serializeOnCreate('custom_fields.tecenc_market_data'); ו-api.serializeToDraft('custom_fields.tecenc_market_data');.

    • רכיב הטופס שלי ב-GJS מעדכן את composerModel.custom_fields.tecenc_market_data.my_field = value; (על ידי קביעת אובייקט custom_fields חדש ליצירת ריאקטיביות).

  2. Backend plugin.rb:
    • מתקשר ל-Topic.register_custom_field_type("tecenc_market_data", :json).
    • מטפל ב-topic_created ומצפה לקרוא נתונים מ-topic.custom_fields["tecenc_market_data"].

שגיאה חדשה שמפסיקה את הפעולה:

על אף הניסיונות לאתחל את custom_fields על ה-composerModel, אני נתקל באופן עקבי בשגיאה הבאה ב-JavaScript כשאני פותח את הטופס ליצירת נושא חדש (על ידי לחיצה על כפתור ה-“Create Topic”):


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

```שגיאה זו מצביעה על כך ש-`this.model.custom_fields` (על `composerModel`) הוא `undefined` או `null` عندما קוד ליבת Discourse ב-`composer.js:1010` (בתוך שיטת `open`, הנקראת על ידי `openNewTopic`) מנסה להגדיר תכונות מקוננות עליו. זה קורה *לפני* שהטופס של התוסף שלי אפילו ניתן להציג.

**שאלות:**

1. בהתחשב בכך שמודל ה-`Composer` של הליבה אמור לאתחל את `custom_fields: {}`, מדוע `composerModel.custom_fields` עדיין יכול להיות לא מוגדר ב-`composer.js:1010` במהלך רצף ה-`openNewTopic` בגרסת Discourse זו?
2. האם השימוש ב-`api.modifyClass("model:composer", ...)` באתחל הוא הדרך הנכונה/האפקטיבית ביותר להבטיח ש-`custom_fields` (וכל מפתחות התוסף המקוננים בו) יאותחלו ככל שניתן מוקדם כדי למנוע שגיאה ליבה זו? אם לא, מה הוא דפוס ההמלצה?
3. האם ייתכן שיש בעיית תזמון שבה צעדי האתחול של ה-`composer` בליבה רצים וצופים ש-`custom_fields` כבר מאותחל לפני שהאתחולים של התוסף המשתמשים ב-`api.modifyClass` עובדו באופן מלא על ה-prototype או על המופעים של `Composer`?

כל תובנה על שגיאה “Property set failed” בליבת ה-`composer` ועל השיטות הטובות ביותר לתוספים כדי לתקשר בבטחה עם `composerModel.custom_fields` במהלך האתחול תהיה מאוד מועילה.

תודה על הזמן שלך!

אני חושב שאתה צריך להוסיף את הנתונים שלך לסיריאלייזר בצד Rails.

האם הנתונים שלך מגיעים ל-Rails? היכן אתה מאחסן אותם?

בדוק תוספים שיש להם addToSerializer.

I am saving the data in a custom database table.

addToSerializer is for moving data from server to client, isnt it?

My problem is moving data from client to server so that I can save it. So I am using serializeOnCreate. But the problem is that the data is not flowing to the opts hash. So, currently, the answer is no, the custom data isn’t making it into the opts hash as expected. Its not getting into rails.

I did look into this educational plugin but not able to make it work for 6 custom fields.

לייק 1

הבנתי איך להריץ את זה.

החסר לי היה השלב הבא: api.serializeToTopic(MY_BLOB_KEY, \topic.${MY_BLOB_KEY}\);

2 לייקים