在插件中使用 tag-chooser

我正在开发一个插件,用于为在特定分类中创建的新主题分配默认标签。

下面的代码在输入用“|”分隔的标签名称时“可以工作”,例如“tag1|tag2”就能用。(我简直不敢相信!不过先不说这个。)

<h3>{{i18n 'topic_default_tag.title'}}</h3>
<section class='field default-tag'>
  <div class="default-tag">
  <label>
    {{input type="list" value=category.custom_fields.default_tag }}
    {{popup-input-tip validation=tagValidation}}
    {{i18n 'topic_default_tag.default_tag'}}
  </label>
  </div>
</section>

现在我希望使用一个真正的标签选择器,而不是未经验证的字符串。看起来下面的代码应该可以工作:

<h3>{{i18n 'topic_default_tag.title'}}</h3>
<section class='field default-tag'>
  <div class="default-tag">
  <label>
    {{tag-chooser tags=category.custom_fields.default_tag tabindex="4" categoryId=category.id}}
    {{popup-input-tip validation=tagValidation}}
    {{i18n 'topic_default_tag.default_tag'}}
  </label>
  </div>
</section>

但实际并不行。我的问题根源在于,我对 Handlebars 的那些“魔法”几乎一无所知。比如,我猜第一部分之所以能工作,是因为它“神奇地”从 <section class='field default-tag'> 中的 default-tag 获取了字段名,但这纯属运气。:slight_smile:

当我使用 tag-chooser 时,标签会以数组形式传递给 Rails,而 Rails 在我将它们转换为用 | 分隔的字符串并写入 CategoryCustomField 之前就丢弃了它们。使用 {{input type=list...}} 时,我可以手动输入用 | 分隔的字符串,一切正常。我是否需要在 Ember 端使用某种“魔法”将数组转换为字符串?

也许我需要参考类似 What's the best approach to access category specific settings? 的做法?

编辑:若要在 webhook 被调用之前添加标签,请使用 after_create 而不是 DiscourseEvent.on(:post_created)。Rails 现在对我来说基本说得通了,但 Ember、JavaScript 和 CSS 就另当别论了。

你可以通过使用 {{log variable_name}} 并检查控制台,来确认所使用的变量是否包含所需的值。如果未将正确的值传递给组件,则无法显示正确的输出。

你也可以在核心代码中进行同样的操作以验证,确认这些值是否正确传递。

如果我的说法听起来有些天真,请见谅。

不!我只是个原始人。

这非常有帮助,因为我之前不知道该如何操作。事实证明,这些字段中确实有数据。而且,标签名称数据确实位于 category.custom_fields.default_tag 中,并已被传递给 mini-tag-choosercategory.id 确实包含类别的 ID,但选择器没有提供任何标签。

我无法判断如果数据存在,是否会被推送到该字段中。

编辑:噢!但使用 tag-chooser 而不是 mini-tag-chooser 可以正常工作(我会相应地修改上面的代码)。唯一的问题是,当我点击保存时,数据并没有被保存。

这一行让我起了疑心。我认为 tags 参数可能期望接收一个数组,而据我看来,你传入的是单个值(这一点需要确认)。如果确实传入的是单个值,请将其改为数组后再试。

好吧,在 Rails 这边,它是一个由 | 分隔的字符串。提交后我看到我输入的内容确实被提交了,例如:"custom_fields"=>{"default_tag"=>["error", "high-availability", "best-practices"]}, "custom_fields"=>{"default_tag"=>["health-checks"]},

以下是完整内容:

Started PUT "/categories/2" for 127.0.0.1 at 2019-08-01 13:56:13 -0700
Processing by CategoriesController#update as */*
  Parameters: {"name"=>"Lounge", "slug"=>"lounge", "color"=>"EEEEEE", "text_color"=>"652D90", "permissions"=>{"trust_level_3"=>"1"}, "auto_close_hours"=>"", "auto_close_based_on_last_post"=>"false", "position"=>"3", "email_in"=>"", "email_in_allow_strangers"=>"false", "mailinglist_mirror"=>"false", "allow_badges"=>"true", "custom_fields"=>{"default_tag"=>["error", "high-availability", "best-practices"]}, "topic_template"=>"", "suppress_from_latest"=>"false", "all_topics_wiki"=>"false", "allow_global_tags"=>"false", "sort_order"=>"", "sort_ascending"=>"", "topic_featured_link_allowed"=>"true", "show_subcategory_list"=>"false", "num_featured_topics"=>"3", "default_view"=>"", "subcategory_list_style"=>"rows_with_featured_topics", "default_top_period"=>"all", "minimum_required_tags"=>"0", "navigate_to_first_post_after_read"=>"false", "search_priority"=>"0", "id"=>"2"}

看起来神秘的那一端确实发生了正确的事情,但 Rails 并没有更新 Category Custom Field。我将回去对比我现有的代码和另一个处理 CategoryCustomField 的插件。

非常感谢,@fzngagan

编辑:嗯。我想问题在于:

Unpermitted parameter: :default_tag

你一定会很享受找到答案的过程。不过,如果你需要帮助,我非常乐意提供。:slight_smile:

是的。Rails 目前还不知道如何处理这个新字段。你需要使用 add_to_serializer 将该新字段添加进去。你可以在添加自定义字段的插件中看到相关示例。

好吧,plugin.rb 包含以下内容:

  Category.register_custom_field_type('default_tag', :list)
  Site.preloaded_category_custom_fields << 'default_tag' if Site.respond_to? :preloaded_category_custom_fields
  add_to_serializer(:basic_category, :default_tag) { object.custom_fields["default_tag"] }

如果你愿意查看,代码位于 https://github.com/pfaffman/discourse-topic-default-tag。

我感到困惑,因为以下代码:

   {{input type="list" value=category.custom_fields.default_tag }}

可以正常工作,但

{{tag-chooser tags=category.custom_fields.default_tag tabindex="4" categoryId=category.id}}

却无法运行。

编辑:看起来使用 tag-chooser 时,我的自定义字段会被作为数组发送到 Rails,然后在处理前被丢弃;而使用 text field 并创建一个以 | 分隔的字符串时,则一切正常。(我曾以为可以在 Rails 端通过 before_validation 来修复这个问题,但并未成功。)

所以我想我需要在 Ember 端做一些“魔法”操作,在数据发送回服务器之前将数组转换为字符串?

我也曾不带参数使用 tag-chooser,它获取了所有可用的标签。

此外,我认为我找到了在使用 tag-chooser 时这些标签未被保存(甚至未到达)Rails 端的原因。当你使用 input type=list 并添加 value 字段时,它会自动成为表单的一部分。

tag-chooser 生成的标记是一个 div。这就是为什么它未被包含在表单提交中的原因。

我正在尝试为其提供一个变通方案。一旦实现,我将提交一个 PR。

我昨天和 @j.jaffeux 谈过,他正在着手进行一项更改。

这正是我正在做的。

太好了。作为一名新手,我正努力不断学习,这对我帮助很大 :slight_smile:

你好 @pfaffman

我成功整理出了一个修复方案。由于 tag-chooser 功能过于出色,我不愿从 Ember 端舍弃它,因此我选择在 Rails 端进行修复。

我在 CategoriesController 中编写了一个简单的函数,并将其设置为 :before_action。在该函数中,我将 default_tag 数组转换为以 | 分隔的字符串。

其次,我将 before_update 改为 before_commit,因为 custom_fields 似乎是在该执行阶段才被设置的。

嗯,我以为我已经试过了,不过到了办公室我会再检查一下。谢谢!

啊哈!这正是我不知道如何做到的。你是把它放在 plugin.rb 里吗?

是的。这对我起作用了。

class ::ApplicationController
    def convert_default_tag
      return unless :topic_default_tag_enabled
      puts 'request'
     # 只需检查字段是否存在,以避免出错
      request.params["custom_fields"]["default_tag"] = request.params["custom_fields"]["default_tag"].join('|')
      p request.params["custom_fields"]["default_tag"]
    end
  end
  require 'categories_controller'
  class ::CategoriesController
     before_action :convert_default_tag , only: [:create]
  end

我认为完全可以在控制器本身定义 default_tag 方法,但我无法让这种方式生效。由于每个 Discourse 控制器都继承自 ApplicationController,该方法在扩展自 ApplicationController 时就会被加载,从而达到了目的。

@merefield
是否可以在控制器本身定义一个名为 :before_action 的方法来调用?

你应该在代码块前后各加三个反引号。

```
 代码放在这里
  ```

我迫不及待想明天试试了!非常感谢!

谢谢。我原以为是单反引号,但卡住了。所以我用了普通的 HTML <pre></pre> 标签来让它生效。

是的,我认为是可以的

这真是帮了大忙。现在我有了这段代码:

  class ::CategoriesController
    before_action :default_tag_to_string, only: [:create, :update]

    def default_tag_to_string
      puts "CDT: #{params}"

      return unless :topic_default_tag_enabled
      # 只需检查字段是否存在,以避免报错
      request.params["custom_fields"]["default_tag"] = request.params["custom_fields"]["default_tag"].join('|')
      p request.params["custom_fields"]["default_tag"]
    end

  end

现在,在 Rails 尝试更新记录之前,选中的标签会被转换为字符串,并且按预期工作。

现在的问题是,我需要弄清楚在 Rails 站点上应该在哪里将这个字符串转换回我们在 JavaScript/Ember 端所需的数组。@j.jaffeux,我不记得(或者更可能的是,我当时没理解)你当时试图解决的问题是在 JavaScript 到 Rails 这一侧重写该值(已解决!),还是在 Rails 到 JavaScript 这一侧(目前仍感到困惑)。

对我有效的方法是:在将标签分配给主题时,将代码中的 :before_update 改为 :before_commit

其次,当你打开编辑分类页面时,默认标签设置字段需要在其 tags 参数中传入一个数组,才能正确显示已选中的标签。