Similar to adding custom fields to a resource, you can add custom tools. A custom tool is a partial added to your resource's
Generate a resource tool
bin/rails generate avo:resource_tool post_info. That will create two files. The configuration file
app/avo/resource_tools/post_info.rb and the partial file
The configuration file holds the tool's name and the partial path if you want to override it.
class PostInfo < Avo::BaseResourceTool self.name = "Post info" # self.partial = "avo/resource_tools/post_info" end
The partial is ready for you to customize further.
<div class="flex flex-col"> <%= render Avo::PanelComponent.new title: "Post info" do |c| %> <% c.tools do %> <%= a_link('/avo', icon: 'heroicons/solid/academic-cap', style: :primary) do %> Dummy link <% end %> <% end %> <% c.body do %> <div class="flex flex-col p-4 min-h-24"> <div class="space-y-4"> <h3>🪧 This partial is waiting to be updated</h3> <p> You can edit this file here <code class='p-1 rounded bg-gray-500 text-white text-sm'>app/views/avo/resource_tools/post_info.html.erb</code>. </p> <p> The resource tool configuration file should be here <code class='p-1 rounded bg-gray-500 text-white text-sm'>app/avo/resource_tools/post_info.rb</code>. </p> <% # In this partial, you have access to the following variables: # tool # @resource # @resource.model # form (on create & edit pages. please check for presence first) # params # Avo::App.context # current_user %> </div> </div> <% end %> <% end %> </div>
You might need access to a few things in the partial.
You have access to the
tool, which is an instance of your tool
PostInfo, and the
@resource, which holds all the information about that particular resource (
params, and others), the
params of the request, the
Avo::App.context and the
current_user. That should give you all the necessary data to scope out the partial content.
The resource tool is default visible on the
Show view of a resource. You can change that using the visibility options (
# app/avo/resources/post_resource.rb class PostResource < Avo::BaseResource tool PostInfo, show_on: :edit end
Using path helpers
Because you're in a Rails engine, you will have to prepend the engine object to the path.
For Avo paths
Instead of writing
resources_posts_path(1) you have to write
For the main app paths
When you want to reference paths from your main app, instead of writing
posts_path(1), you have to write
Add custom fields on forms
From Avo 2.12
You might want to add a few more fields or pieces of functionality besides the CRUD-generated fields on your forms. Of course, you can already create new custom fields to do it in a more structured way, but you can also use a resource tool to achieve more custom behavior.
You have access to the
form object that is available on the new/edit pages on which you can attach inputs of your choosing. You can even achieve nested form functionality.
You have to follow three steps to enable this functionality:
- Add the inputs in a resource tool and enable the tool on the form pages
- Tell Avo which
paramsit should permit to write to the model
- Make sure the model is equipped to receive the params
In the example below, we'll use the
FishResource, add a few input fields (they will be a bit unstyled because this is not the scope of the exercise), and do some actions with some of them.
We first need to generate the tool with
bin/rails g avo:resource_tool fish_information and add the tool to the resource file.
class FishResource < Avo::BaseResource tool FishInformation, show_on: :forms end
_fish_information.html.erb partial, we'll add a few input fields. Some are directly on the
form, and some are nested with
The fields are:
fish_typeas a text input
propertiesas a multiple text input which will produce an array in the back-end
informationas nested inputs which will produce a
Hashin the back-end
<!-- _fish_information.html.erb --> <div class="flex flex-col"> <%= render Avo::PanelComponent.new(title: @resource.model.name) do |c| %> <% c.tools do %> <%= a_link('/admin', icon: 'heroicons/solid/academic-cap', style: :primary) do %> Primary <% end %> <% end %> <% c.body do %> <div class="flex flex-col p-4 min-h-24"> <div class="space-y-4"> <% if form.present? %> <%= form.label :fish_type %> <%= form.text_field :fish_type, value: 'default type of fish', class: input_classes %> <br> <%= form.label :properties %> <%= form.text_field :properties, multiple: true, value: 'property 1', class: input_classes %> <%= form.text_field :properties, multiple: true, value: 'property 2', class: input_classes %> <br> <% form.fields_for :information do |information_form| %> <%= form.label :information_name %> <%= information_form.text_field :name, value: 'information name', class: input_classes %> <div class="text-gray-600 mt-2 text-sm">This is going to be passed to the model</div> <br> <%= form.label :information_history %> <%= information_form.text_field :history, value: 'information history', class: input_classes %> <div class="text-gray-600 mt-2 text-sm">This is going to be passed to the model</div> <br> <%= form.label :information_age %> <%= information_form.text_field :age, value: 'information age', class: input_classes %> <div class="text-gray-600 mt-2 text-sm">This is NOT going to be passed to the model</div> <% end %> <% end %> </div> </div> <% end %> <% end %> </div>
Next, we need to tell Avo and Rails which params are welcomed in the
update request. We do that using the
extra_params option on the
FishResource. Avo's internal implementation is to assign the attributes you specify here to the underlying model (
class FishResource < Avo::BaseResource self.extra_params = [:fish_type, :something_else, properties: , information: [:name, :history]] tool FishInformation, show_on: :forms end
The third step is optional. You must ensure your model responds to the params you're sending. Our example should have the
information attributes or setter methods on the model class. We chose to add setters to demonstrate the params are called to the model.
class Fish < ApplicationRecord self.inheritance_column = nil # required in order to use the type DB attribute def fish_type=(value) self.type = value end def properties=(value) # properties should be an array puts ["properties in the Fish model->", value].inspect end def information=(value) # properties should be a hash puts ["information in the Fish model->", value].inspect end end
If you run this code, you'll notice that the
information.information_age param will not reach the
information= method because we haven't allowed it in the