Many a times, depending on the requirements, it makes sense to store unstructured json data in database fields. PostgreSQL recognizes this requirement and provides a dedicated json field that automatically handles JSON validation. As has been outlined in the RoR Guides , it is pretty simple to take advantage of this feature from Rails. However if you also use ActiveAdmin to manage your admin interface, you will quickly find out that library Formtastic that ActiveAdmin uses to manage its forms, leaves a lot to be desired when it comes to JSON editing support.
In this post we outline a simple approach to improve JSON editing support in ActiveAdmin using the excellent JSON editor widget by Jos de Jong. It is worth pointing out that our implementation has very little to do with PostgreSQL and may be used without modifications if you are storing JSON in say MySQL text fields. Of course you will need to handle server side validation yourself in that case.
The source code for the post is available on Github.
Let us have a simple product model with following schema:
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.text :description
t.json :metadata
t.timestamps
end
end
end
You may expect providing admin support for this model will just be
a matter of adding a file app/admin/product.rb
:
ActiveAdmin.register Product do
permit_params :name, :description, :metadata
end
However the moment you try to create a new instance, you will be greeted with an error message:
So basically Formtastic has no input field pre-configured for json field. A rudimentary workaround is fairly simple - We explicitly ask it to use a textarea for metadata field
ActiveAdmin.register Product do
permit_params :name, :description, :metadata
form do |f|
f.inputs do
f.input :name
f.input :description
f.input :metadata, as: :text
end
f.actions
end
end
This does the job:
But seriously, if you have to edit this json very frequently or manage large json entries, a simple textarea is not an ideal solution. Plus if you accidentally enter some invalid json, You will be provided with a feedback only post submission:
The JSON editor widget by Jos de Jong provides a lot better json editing interface. You can try it out online.
If you like what you see, you will be pleased to find that the widget is pretty easy to integrate right inside ActiveAdmin.
Let us first configure our form to add a class to the json field so that we can handle json input fields in a generic fashion.
ActiveAdmin.register Product do
permit_params :name, :description, :metadata
form do |f|
f.inputs do
f.input :name
f.input :description
f.input :metadata, as: :text, input_html: { class: 'jsoneditor-target' }
end
f.actions
end
end
Next we will need to download the relevant files and add to our vendor directory. I have already changed the files to use sprockets urls, so you can grab the files form the repo.
Next we modify the active_admin.js.coffee:
#= require active_admin/base
#= require jsoneditor
#= require jsoneditor_activeadmin_integration
Once we have the required files in place, integration is pretty simple -
app/assets/javascripts/jsoneditor_activeadmin_integration
:
$ ->
$('.jsoneditor-target').each ->
target = $ this
container = $('<div class="jsoneditor-container">')
.insertAfter target
editor = new JSONEditor container[0],
modes: ['code', 'form', 'text', 'tree', 'view']
change: ->
target.val editor.get()
editor.set(
try
JSON.parse target.val()
)
target.hide()
This simply hides the textarea for json field, and adds a json editor widget. When the editor is updated, the hidden textarea is updated with the new value - so our form continues to work just as expected, without Formtastic having to be aware of the widget at all.
I had to explicitly override some of the conflicting styles from ActiveAdmin which were messing up the Editor Widget css:
.jsoneditor-container, .jsoneditor-contextmenu {
table {
width: auto;
margin: 0;
}
.jsoneditor {
background: white;
}
button, button:hover, .menu button, .menu button:hover {
background: none;
text-shadow: none;
box-shadow: none;
border-radius: 0;
}
}
.jsoneditor-container {
margin-left: 20%;
width: 80%;
}
And we are pretty much done:
I realize that the default styling of the widget sticks out a bit against the default styling of ActiveAdmin page, but all that is needed to rectify is a few CSS rules which I leave as an exercise for the reader.
As always, any feedback and suggestions are more than welcome.