If you have a separate model for a Tag and manage them separately, it is pretty easy to attach those tags to another model via a field „tags_ids“. But, let’s say, you’ve got some SEO-settings for a object. It wouldn’t make much sense to store tags in a separate model for all your other pages.
To store tags as a property of any given object we will utilise PostgreSQL’s ability to have an array as data type. So, first of all, I don’t want to add all SEO-fields to every other object over and over again. Let’s create a separate SEO-model:
class CreateSeos < ActiveRecord::Migration[5.1] def change create_table :seos do |t| t.string :title t.text :description t.string :image t.timestamps end end end
Then add the keyword column:
class AddKeywordsToSeos < ActiveRecord::Migration[5.1] def change add_column :seos, :keywords, :string, array: true, default: [] end end
Add references to appropriate other tables:
class AddReferencesToSeos < ActiveRecord::Migration[5.1] def change add_reference :seos, :post, foreign_key: true add_reference :seos, :product, foreign_key: true end end
For front-end editing I’m using bootstrap-tagsinput (here is an example). Install it via yarn add bootstrap-tagsinput
(or download files manually). Don’t forget to include JS aswell as CSS files.
On the front-end part it is important to use a select field instead of simple text_field to be able to select multiple tags. Here is my full html code for the seo block:
=f.fields_for :seo do |seo_builder| .form-group %label.col-lg-2.control-label =t('admin.form.model.seo.title') .col-lg-10 =seo_builder.text_field :title, class: 'form-control' .hr-line-dashed .form-group %label.col-lg-2.control-label =t('admin.form.model.seo.description') .col-lg-10 =seo_builder.text_area :description, class: 'form-control', rows: 4 .form-group %label.col-lg-2.control-label =t('admin.form.model.seo.keywords') .col-lg-10 =seo_builder.select :keywords, object.seo.keywords.map {|i| [i, i]}, {}, {id: 'object_keywords', class: 'form-control custom-bootstrap-tagsinput', data: {role: "tagsinput"}, multiple: true}
For the backend part include seo to your corresponding models:
class Post < ApplicationRecord has_one :seo accepts_nested_attributes_for :seo, allow_destroy: true end
In your controller, whitelist the attributes (important to use keywords: []
to indicate an array) and don’t forget to build
the fields if they aren’t present yet.
seo_attributes: [:id, :title, :description, :image, keywords: []]
def new @post = Post.new if @post.seo.blank? @post.build_seo end @tags = @post.seo.keywords || "" end def edit if @post.seo.blank? @post.build_seo end @tags = @post.seo.keywords || "" end
Now you can enjoy setting any variable tags and them being persistent in your database. Happy coding!