Использование 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. Например, я предполагаю, что первый вариант работает, потому что он каким-то чудом получает имя поля из default-tag в <section class='field default-tag'>, но это была просто удача. :slight_smile:

Когда я использую tag-chooser, теги передаются в Rails как массив, который он отбрасывает, прежде чем я успеваю преобразовать их в строку с разделителем | для записи в CategoryCustomField. С {{input type=list...}} я могу сам ввести строку с разделителем |, и всё работает отлично. Нужно ли мне какое-то «магическое» решение Ember для преобразования массива в строку на стороне Ember?

Возможно, мне стоит сделать что-то вроде What's the best approach to access category specific settings??

EDIT: Чтобы добавлять теги до вызова вебхука, используйте after_create вместо DiscourseEvent.on(:post_created). Rails теперь в целом понятен, но с Ember, JavaScript и CSS у меня всё ещё возникают трудности.

Вы можете проверить, содержат ли используемые вами переменные необходимые значения, используя {{log variable_name}} и проверяя консоль. Если правильные значения не передаются в компонент, он не будет отображать правильный вывод.

Вы можете проверить это, выполнив то же самое в основном коде, чтобы подтвердить, что значения передаются корректно.

Извините, если это звучит наивно.

Нет! Я просто первобытный человек.

Это очень помогло, так как я не знал, как это сделать. Оказывается, у меня действительно есть данные в этих полях. И данные тега находятся в category.custom_fields.default_tag и передаются в mini-tag-chooser. category.id содержит ID категории, но выборщик не предлагает никаких тегов.

Я не могу сказать, попали бы данные в поле, если бы это было так.

РЕДАКТИРОВАНИЕ: О-о-о! Но использование tag-chooser вместо mini-tag-chooser работает как положено (я отредактирую код выше соответственно). Единственная проблема в том, что данные не сохраняются при нажатии кнопки «Сохранить».

Эта строка вызывает у меня подозрения. Мне кажется, что параметр tags принимает массив, а судя по всему, вы передаёте одно значение (это нужно подтвердить). Если передаётся одно значение, просто оберните его в массив и попробуйте снова.

Что ж, на стороне Rails это строка, разделённая символом |, и я вижу, что после отправки введённые данные отправляются, например: "custom_fields"=>{"de fault_tag"=>["error", "high-availability", "best-practices"]}, и "custom_fields"=>{"de fault_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"=>{"trus
t_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"=>{"de
fault_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_feature
d_link_allowed"=>"true", "show_subcategory_list"=>"false", "num_featured_topics"=>"3", "default_view"=>"", "subcate
gory_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 не обновляет пользовательское поле категории. Я вернусь к сравнению того, что у меня есть, с другим плагином, работающим с 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"] }

Код находится здесь: GitHub - pfaffman/discourse-topic-default-tag: Allow topics to include default tags · GitHub, если вы захотите посмотреть.

Я в замешательстве: вот это

   {{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 как массив, а затем отбрасывается до обработки. Однако, если использовать текстовое поле, где я создаю строку, разделенную символом |, всё работает отлично. (Я думал, что смогу исправить это с помощью before_validation на стороне Rails, но безрезультатно.)

Так что,看来, мне нужно какое-то волшебство на стороне Ember, чтобы преобразовать массив в строку перед отправкой обратно?

Я тоже использовал tag-chooser без параметров, и он получил все доступные теги.

Кроме того, я думаю, что нашёл причину, по которой они не сохраняются (или даже не доходят) :wink: на стороне Rails при использовании tag-chooser. Когда вы используете input type list и добавляете поле value, оно автоматически становится частью формы.

Но разметка, генерируемая tag-chooser, — это div. Вот почему оно не добавляется при отправке формы.

Я пытаюсь придумать обходное решение. Как только оно заработает, я отправлю PR.

Я говорил вчера с @j.jaffeux, и он работает над внесением изменений.

Именно этим я и занимаюсь.

Это отлично. Как новичок, я стараюсь узнавать всё больше и больше, и это очень помогает :slight_smile:

Привет, @pfaffman.

Мне удалось подготовить исправление. Сделать это со стороны Ember не получилось, так как компонент tag-chooser казался слишком полезным, чтобы от него отказываться, но я реализовал решение на стороне 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 непосредственно в контроллере, но у меня не получилось заставить это работать таким образом. Поскольку ApplicationController является базовым классом, от которого наследуются все контроллеры Discourse, метод загружается при расширении класса от 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-to-Rails (исправлено!) или на стороне Rails-to-JavaScript (всё ещё запутался).

Мне помогло следующее: я заменил :before_update на :before_commit в вашем коде при назначении тегов теме.

Во-вторых, когда вы открываете страницу редактирования категории, поле настроек тегов по умолчанию требует массив в параметре tags, чтобы отображать выбранные теги.