I'm using @yairEO/tagify
on my Rails 7 app. I have it almost working. It will save to the database as valid JSON. If I submit the form using tagify with 3 tags, for example tag1
, tag2
, tag3
, after I go back to the form to edit, tagify shows these 3 separate tags combined into one single tag.
From the database:
[
{
"value":"tag1"
},
{
"value":"tag2"
},
{
"value":"tag3"
}
]
From irb recipe.tags
:
[
{
"value"=>"tag1"
},
{
"value"=>"tag2"
},
{
"value"=>"tag3"
}
]
Things quickly get out of control when adding to the bad response. Before submitting:
application.js
import Tagify from "@yaireo/tagify"
document.addEventListener('turbo:load', (event) => {
new Tagify(document.querySelector('#recipe_tags'));
});
recipes_controller.rb
def update
@recipe.update(tags: JSON.parse(params[:recipe][:tags]))
if @recipe.update(recipe_params)
redirect_to @recipe
else
render :edit, status: :unprocessable_entity
end
end
def recipe_params
params[:recipe][:tags] = JSON.parse(params[:recipe][:tags])
params.require(:recipe).permit(
:title, :subtitle, :tags
)
end
edit.html.erb
<div>
<%= form.label :tags %><br>
<%= form.text_field :tags %>
<% recipe.errors.full_messages_for(:tags).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
schema.rb
create_table "recipes", force: :cascade do |t|
t.string "title"
t.string "subtitle"
t.jsonb "tags", default: {}, null: false
t.index ["user_id"], name: "index_recipes_on_user_id"
end
The problem was Rails was sending back a Hash, but the form wanted JSON.
To fix this, I converted the data brought into the form to json:
<%= form.label tags %>
<%= form.text_field(tags, value: recipe[tags].to_json) %>
Also, to DRY this up, I went ahead and removed these from the recipes_controller.rb
:
@recipe.update(tags: JSON.parse(params[:recipe][:tags]))
and
params[:recipe][:tags] = JSON.parse(params[:recipe][:tags])
Then added this to the recipe model:
protected
def convert_to_json
self.tags = JSON.parse(tags) unless tags.empty?
end
but we'd need to make sure it's valid JSON before converting, otherwise we end up with the same mess we had in the first place:
private
def valid_json?(value)
result = JSON.parse(value)
result.is_a?(Hash) || result.is_a?(Array)
rescue JSON::ParserError, TypeError
false
end
then convert_to_json
becomes ... unless tags.empty? || !valid_json?(tags)
Seems to be working properly now! If anyone has any tips on how to make this even cleaner/faster/better, let us know. Thanks