# Aangepaste gegevens van GJS composer formulier niet in \`opts\` voor \`:topic\_created\` event

**URL:** https://meta.discourse.org/t/custom-data-from-gjs-composer-form-not-in-opts-for-topic-created-event/366809
**Category:** Development
**Created:** [20 mei 2025 om 06:17 UTC](https://meta.discourse.org/t/custom-data-from-gjs-composer-form-not-in-opts-for-topic-created-event/366809 "2025-05-20T06:17:38Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![SubStrider](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/substrider/32/512604_2.png) [@SubStrider](https://meta.discourse.org/u/SubStrider)
#### Post date: [20 mei 2025 om 06:17 UTC](https://meta.discourse.org/t/custom-data-from-gjs-composer-form-not-in-opts-for-topic-created-event/366809/1 "2025-05-20T06:17:38Z")

</div>

This is in continuation to this- [Custom topic fields per category or custom topic entry form per category?](https://meta.discourse.org/t/custom-topic-fields-per-category-or-custom-topic-entry-form-per-category/355260)

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:

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:**

```ruby
# 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!

---

<div class="post-metadata">

### Author: ![SubStrider](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/substrider/32/512604_2.png) [@SubStrider](https://meta.discourse.org/u/SubStrider)
#### Post date: [20 mei 2025 om 10:22 UTC](https://meta.discourse.org/t/custom-data-from-gjs-composer-form-not-in-opts-for-topic-created-event/366809/2 "2025-05-20T10:22:19Z")

</div>

Hi again,

Following up on my previous issue about data not reaching the `opts` hash, I’ve shifted my strategy to use the recommended `custom_fields` pathway for serializing data from my plugin’s composer form.

**Current Approach:**

1. **Frontend Initializer:**
  - I’m attempting to ensure `composerModel.custom_fields` and `composerModel.custom_fields["tecenc_market_data"]` (my plugin’s data key) are initialized as objects. My latest attempt involves using `api.modifyClass("model:composer", ...)` to add initialization logic to `init()` and `clearState()`:

  - I then use `api.serializeOnCreate('custom_fields.tecenc_market_data');` and `api.serializeToDraft('custom_fields.tecenc_market_data');`.

  - My GJS form component updates `composerModel.custom_fields.tecenc_market_data.my_field = value;` (by setting a new `custom_fields` object for reactivity).

2. **Backend `plugin.rb`:**
  - `Topic.register_custom_field_type("tecenc_market_data", :json)` is called.
  - The `:topic_created` event handler expects to read data from `topic.custom_fields["tecenc_market_data"]`.

**New Blocking Error:**

Despite these attempts to initialize `custom_fields` on the `composerModel`, I am now consistently encountering the following JavaScript error when trying to open the composer for a new topic (by clicking the “Create Topic” button):

```plaintext

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 error indicates that `this.model.custom_fields` (on the `composerModel`) is `undefined` or `null` when core Discourse code at `composer.js:1010` (within the `open` method, called by `openNewTopic`) attempts to set a nested property on it. This happens _before_ my plugin’s form can even be rendered.

**Questions:**

1. Given the core `Composer` model should initialize `custom_fields: {}`, why might `composerModel.custom_fields` still be undefined at `composer.js:1010` during the `openNewTopic` sequence in this Discourse version?
2. Is the `api.modifyClass("model:composer", ...)` approach in an initializer the correct/most effective way to ensure `custom_fields` (and nested plugin keys within it) are initialized as objects early enough to prevent this core error? If not, what is the recommended pattern?
3. Could there be a timing issue where core composer initialization steps are running and expecting `custom_fields` before plugin initializers using `api.modifyClass` have fully taken effect on the `Composer` model prototype or instances?

Any insights into this “Property set failed” error in the composer’s core and the best practices for plugins to safely interact with `composerModel.custom_fields` during initialization would be extremely helpful.

Thanks for your time!

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [20 mei 2025 om 22:14 UTC](https://meta.discourse.org/t/custom-data-from-gjs-composer-form-not-in-opts-for-topic-created-event/366809/3 "2025-05-20T22:14:42Z")

</div>

I think you need to add your data to the serializer on the rails side.

Is your data getting in to rails? Where are you storing it?

Look at plugins that have addToSerializer.

---

<div class="post-metadata">

### Author: ![SubStrider](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/substrider/32/512604_2.png) [@SubStrider](https://meta.discourse.org/u/SubStrider)
#### Post date: [21 mei 2025 om 03:49 UTC](https://meta.discourse.org/t/custom-data-from-gjs-composer-form-not-in-opts-for-topic-created-event/366809/4 "2025-05-21T03:49:48Z")

</div>

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.

[https://github.com/pavilionedu/discourse-topic-custom-fields](https://github.com/pavilionedu/discourse-topic-custom-fields)

---

<div class="post-metadata">

### Author: ![SubStrider](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/substrider/32/512604_2.png) [@SubStrider](https://meta.discourse.org/u/SubStrider)
#### Post date: [21 mei 2025 om 04:18 UTC](https://meta.discourse.org/t/custom-data-from-gjs-composer-form-not-in-opts-for-topic-created-event/366809/5 "2025-05-21T04:18:21Z")

</div>

Got it to work.

I was missing this step: `api.serializeToTopic(MY_BLOB_KEY, \topic.${MY_BLOB_KEY}\);`
