这是对此的延续 - Custom topic fields per category or custom topic entry form per category?
我决定创建这个插件。表单组件本身通过 api.renderInOutlet 正确渲染。但是数据没有在后端保存到自定义表中。
前端设置:
-
我使用 api.renderInOutlet("composer-fields", MyOutletConnectorComponent) 在指定类别中创建新主题时渲染 GJS 组件 (MyOutletConnectorComponent)。
-
此 MyOutletConnectorComponent 然后渲染我的主 GJS 表单组件 (MyMarketFormComponenent)。
-
在 MyMarketFormComponenent 中,用户输入会更新一个 JavaScript 对象,然后该对象被设置为 Outlet 参数提供的 composerModel 的顶层属性。例如:
// 在 MyMarketFormComponenent 操作中
const currentData = this.args.composerModel.get("market_listing_data") || {};
const updatedData = { ...currentData, [fieldKey]: newValue };
this.args.composerModel.set("market_listing_data", updatedData);
-
在我的插件初始化程序 (tecenc-market-composer.js) 中,我使用 api.serializeOnCreate("market_listing_data", "market_listing_data"); 来尝试将此数据传递到后端。我也使用 api.serializeToDraft("market_listing_data", "market_listing_data");。
后端问题:
在我的 plugin.rb 中,在 DiscourseEvent.on(:topic_created) do |topic, opts, user| ... end 处理程序中:
- 我检查主题是否在正确的类别中(这有效)。
- 然后我尝试使用
market_data = opts[:market_listing_data](或 opts["market_listing_data"])访问序列化数据。
- 问题是
opts[:market_listing_data] 始终为 nil。
- 记录
opts.keys 确认我的 market_listing_data 键不在 opts 哈希的顶层。opts 哈希本身包含标准键,如 :raw、:title、:category、:archetype 等。
我的 plugin.rb 代码片段用于事件:
# plugin.rb
# ...
module ::TecencMarket
SERIALIZED_DATA_KEY = "market_listing_data".freeze
end
# ...
after_initialize do
# ...
DiscourseEvent.on(:topic_created) do |topic, opts, user|
# ... (类别检查逻辑) ...
# 这是 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)
# ... 处理并将数据保存到自定义表 ...
else
Rails.logger.warn "[MyPlugin] Market data not found in opts as expected."
end
end
# ...
end
问题:
- 确保通过 GJS 组件设置为
composerModel 的顶层属性(例如 composerModel.set("my_plugin_data_key", { ... });)被 api.serializeOnCreate 正确序列化并可在后端 :topic_created 事件的 opts 哈希中访问的正确且最可靠的方法是什么?
api.serializeOnCreate("my_key", "my_key") 是否适用于从 GJS 上下文设置在 composerModel 上的任意顶层键,还是通过 custom_fields 进行序列化(例如,设置 composerModel.custom_fields.my_plugin_key = {...} 并使用 api.serializeOnCreate("custom_fields.my_plugin_key"))是这种类型的数据传输到新主题的唯一健壮/推荐方法?
- 在使用由 GJS 组件管理的数据时,使用
api.serializeOnCreate 是否有任何特定的注意事项或常见陷阱可能导致数据未包含在 opts 哈希中?
我已经确认前端表单正在正确收集数据并将其设置在 composerModel.market_listing_data 属性上。问题似乎纯粹在于如何将此数据序列化并传递到后端事件。
任何关于现代 Discourse 中推荐模式的指导或示例都将不胜感激!
谢谢!
您好,
关于我之前关于数据未到达 opts 哈希的问题的后续,我已将策略调整为使用推荐的 custom_fields 路径,从我的插件的 composer 表单中序列化数据。
当前方法:
- 前端初始化程序:
-
我试图确保 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 对象以实现响应性)来更新。
- 后端
plugin.rb:
- 调用
Topic.register_custom_field_type("tecenc_market_data", :json)。
topic_created 事件处理程序期望从 topic.custom_fields["tecenc_market_data"] 读取数据。
新的阻塞性错误:
尽管我尝试在 composerModel 上初始化 custom_fields,但我在尝试打开新话题的 composer 时(点击“创建话题”按钮),不断遇到以下 JavaScript 错误:
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
```""
这个错误表明在 Discourse 核心代码在 `composer.js:1010`(在 `open` 方法中,由 `openNewTopic` 调用)试图在 `composerModel` 上设置嵌套属性时,`this.model.custom_fields`(在 `composerModel` 上)为 `undefined` 或 `null`。这种情况发生在我的插件表单甚至还没有渲染之前。
**问题:**
1. 考虑到核心的 `Composer` 模型应初始化 `custom_fields: {}`,为什么在此 Discourse 版本的 `openNewTopic` 序列中,`composerModel.custom_fields` 在 `composer.js:1010` 时仍为未定义?
2. 在初始化器中使用 `api.modifyClass("model:composer", ...)` 方法是不是确保 `custom_fields`(以及其中的嵌套插件键)提前初始化为对象的正确/最有效方式,以避免这个核心错误?如果不是,推荐的模式是什么?
3. 是否存在时间点上的问题,核心的 Composer 初始化步骤在 `custom_fields` 被期待存在之前就已经运行或期待它已经被初始化,而插件通过 `api.modifyClass` 进行的初始化还没有在 `Composer` 模型的原型或实例上完全生效?
任何关于核心中的 “Property set failed” 错误的见解,以及插件在初始化过程中安全地与 `composerModel.custom_fields` 交互的最佳实践都将非常有帮助。
感谢您的时间!
pfaffman
(Jay Pfaffman)
3
我认为你需要在Rails端的序列化器中添加你的数据。
你的数据有没有传到Rails?你把它存在哪里?
看看那些具有addToSerializer功能的插件。
我正在将数据保存在自定义数据库表中。
addToSerializer 是用于将数据从服务器移动到客户端的,对吧?
我的问题是将数据从客户端移动到服务器,以便我可以保存它。所以我正在使用 serializeOnCreate。但问题是数据没有流向 opts 哈希。所以,目前,答案是否,自定义数据并未按预期进入 opts 哈希。它没有进入 Rails。
我确实查看了这个教育插件,但无法为 6 个自定义字段使其正常工作。
1 个赞
我明白了。
我遗漏了这一步:api.serializeToTopic(MY_BLOB_KEY, \topic.${MY_BLOB_KEY}\);
2 个赞