Field options
Declaring fields
Each Avo resource has a field method that registers your Resource's fields. Avo ships with various simple fields like text, textarea, number, password, boolean, select, and more complex ones like markdown, key_value, trix, and code.
We can use the field method like so:
field :name, as: :textThe name property is the column in the database where Avo looks for information or a property on your model.
That will add a few fields in your admin panel. On the Index view, we will get a new text column. On the Show view, we will also get a text value of that record's database value. Finally, on the Edit and New views, we will get a text input field that will display & update the name field on that model.
Field conventions
When we declare a field, we pinpoint the specific database row for that field. Usually, that's a snake case value.
Each field has a label. Avo will convert the snake case name to a humanized version. In the following example, the is_available field will render the label as Is available.
field :is_available, as: :boolean
INFO
If having the fields stacked one on top of another is not the right layout, try the resource-sidebar.
Change field name
To customize the label, you can use the name property to pick a different label.
field :is_available, as: :boolean, name: 'Availability'
Showing / Hiding fields on different views
There will be cases where you want to show fields on different views conditionally. For example, you may want to display a field in the New and Edit views and hide it on the Index and Show views.
For scenarios like that, you may use the visibility helpers hide_on, show_on, only_on, and except_on methods. Available options for these methods are: :new, :edit, :index, :show, :forms (both :new and :edit) and :all (only for hide_on and show_on).
Be aware that a few fields are designed to override those options (ex: the id field is hidden in Edit and New).
field :body, as: :text, hide_on: [:index, :show]Field Visibility
You might want to restrict some fields to be accessible only if a specific condition applies. For example, hide fields if the user is not an admin.
You can use the visible block to do that. It can be a boolean or a lambda. Inside the lambda, we have access to the context object and the current resource. The resource has the current model object, too (resource.model).
field :is_featured, as: :boolean, visible: -> (resource:) { context[:user].is_admin? } # show field based on the context object
field :is_featured, as: :boolean, visible: -> (resource:) { resource.name.include? 'user' } # show field based on the resource name
field :is_featured, as: :boolean, visible: -> (resource:) { resource.model.published_at.present? } # show field based on a model attributeUsing if for field visibility
You might be tempted to use the if statement to show/hide fields conditionally. However, that's not the best choice because the fields are registered at boot time, and some features are only available at runtime. Let's take the context object, for example. You might have the current_user assigned to the context, which will not be present at the app's boot time. Instead, that's present at request time when you have a request present from which you can find the user.
# ❌ Don't do
class CommentResource < Avo::BaseResource
field :id, as: :id
if context[:current_user].admin?
field :body, as: :textarea
field :tiny_name, as: :text, only_on: :index, as_description: true
end
end
# ✅ Do instead
class CommentResource < Avo::BaseResource
field :id, as: :id
with_options visible: -> (resource:) { context[:current_user].admin?} do
field :body, as: :textarea
field :tiny_name, as: :text, only_on: :index, as_description: true
end
endSo now, instead of relying on a request object unavailable at boot time, you can pass it a lambda function that will be executed on request time with all the required information.
Since 2.30.2
On form submissions, the visible block is evaluated in the create and update controller actions. That's why you have to check if the resource.model object is present before trying to use it.
# `resource.model` is nil when submitting the form on resource creation
field :name, as: :text, visible -> (resource: ) { resource.model.enabled? }
# Do this instead
field :name, as: :text, visible -> (resource: ) { resource.model&.enabled? }Computed Fields
You might need to show a field with a value you don't have in a database row. In that case, you may compute the value using a block that receives the model (the actual database record), the resource (the configured Avo resource), and the current view. With that information, you can compute what to show on the field in the Index and Show views.
field 'Has posts', as: :boolean do |model, resource, view|
model.posts.present?
rescue
false
endThis example will display a boolean field with the value computed from your custom block.
Fields Formatter
Sometimes you will want to process the database value before showing it to the user. You may do that using format_using block.
Notice that this block will have effect on all views.
You have access to a bunch of variables inside this block, all the defaults that Avo::ExecutionContext provides plus value, model, key, resource, view and field.
WARNING
We removed the value argument from format_using since version 2.36.
field :is_writer, as: :text, format_using: -> {
if view == :new || view == :edit
value
else
value.present? ? '👍' : '👎'
end
}This example snippet will make the :is_writer field generate 👍 or 👎 emojis instead of 1 or 0 values on display views and the values 1 or 0 on form views.

Another example:
field :company_url,
as: :text,
format_using: -> {
link_to(value, value, target: "_blank")
} do |model, *args|
main_app.companies_url(model)
endFormatting with Rails helpers
You can also format using Rails helpers like number_to_currency (note that view_context is used to access the helper):
field :price, as: :number, format_using: -> { view_context.number_to_currency(value) }Modify the value before saving it to the database
Since v2.36Similar to format_using we added update_using which will process the value sent from the UI before setting it on the model.
# Cast the text version of the field to actual JSOn to save to the database.
field :metadata, as: :code, update_using: -> {
# You have access to the following variables:
# - value
# - resource
# - record
# - view
# - view_context
# - context
# - params
# - request
ActiveSupport::JSON.decode(value)
}Sortable fields
One of the most common operations with database records is sorting the records by one of your fields. For that, Avo makes it easy using the sortable option.
Add it to any field to make that column sortable in the Index view.
field :name, as: :text, sortable: true
Custom sortable block
When using computed fields or belongs_to associations, you can't set sortable: true to that field because Avo doesn't know what to sort by. However, you can use a block to specify how the records should be sorted in those scenarios.
class UserResource < Avo::BaseResource
field :is_writer,
as: :text,
sortable: ->(query, direction) {
# Order by something else completely, just to make a test case that clearly and reliably does what we want.
query.order(id: direction)
},
hide_on: :edit do |model, resource, view, field|
model.posts.to_a.size > 0 ? "yes" : "no"
end
endThe block receives the query and the direction in which the sorting should be made and must return back a query.
In the example of a Post that has_many Comments, you might want to order the posts by which one received a comment the latest.
You can do that using this query.
class PostResource < Avo::BaseResource
field :last_commented_at,
as: :date,
sortable: ->(query, direction) {
query.includes(:comments).order("comments.created_at #{direction}")
}
endclass Post < ApplicationRecord
has_many :comments
def last_commented_at
comments.last&.created_at
end
endPlaceholder
Some fields support the placeholder option, which will be passed to the inputs on Edit and New views when they are empty.
field :name, as: :text, placeholder: 'John Doe'
Required
When you want to mark a field as mandatory, you may use the required option to add an asterisk to that field, indicating that it's mandatory.
field :name, as: :text, required: true
WARNING
This option is only a cosmetic one. It will not add the validation logic to your model. You must add that yourself (validates :name, presence: true).
INFO
For Avo version 2.14 and higher Avo will automatically detect your validation rules and mark the field as required by default.
You may use a block as well. It will be executed in the ViewRecordHost and you will have access to the view, record, params, context, view_context, and current_user.
field :name, as: :text, required: -> { view == :new } # make the field required only on the new view and not on editReadonly
When you need to prevent the user from editing a field, the readonly option will render it as disabled on New and Edit views and the value will not be passed to that record in the database. This prevents a bad actor to go into the DOM, enable that field, update it, and then submit it, updating the record.
field :name, as: :text, readonly: true
Readonly as a block
Since v2.14You may use a block as well. It will be executed in the ViewRecordHost and you will have access to the view, record, params, context, view_context, and current_user.
field :id, as: :number, readonly: -> { view == :edit } # make the field readonly only on the new edit viewDisabled
When you need to prevent the user from editing a field, the disabled option will render it as disabled on New and Edit views. This does not, however, prevent the user from enabling the field in the DOM and send an arbitrary value to the database.
field :name, as: :text, disabled: true
Default Value
When you need to give a default value to one of your fields on the New view, you may use the default block, which takes either a fixed value or a block.
# using a value
field :name, as: :text, default: 'John'
# using a callback function
field :level, as: :select, options: { 'Beginner': :beginner, 'Advanced': :advanced }, default: -> { Time.now.hour < 12 ? 'advanced' : 'beginner' }Help text
Sometimes you will need some extra text to explain better what the field is used for. You can achieve that by using the help method. The value can be either text or HTML.
# using the text value
field :custom_css, as: :code, theme: 'dracula', language: 'css', help: "This enables you to edit the user's custom styles."
# using HTML value
field :password, as: :password, help: 'You may verify the password strength <a href="http://www.passwordmeter.com/">here</a>.'
INFO
Since version 2.19, the default block is being evaluated in the ResourceViewRecordHost.
Nullable
When a user uses the Save button, Avo stores the value for each field in the database. However, there are cases where you may prefer to explicitly instruct Avo to store a NULL value in the database row when the field is empty. You do that by using the nullable option, which converts nil and empty values to NULL.
You may also define which values should be interpreted as NULL using the null_values method.
# using default options
field :updated_status, as: :status, failed_when: [:closed, :rejected, :failed], loading_when: [:loading, :running, :waiting], nullable: true
# using custom null values
field :body, as: :textarea, nullable: true, null_values: ['0', '', 'null', 'nil', nil]Link to resource
Sometimes, on the Index view, you may want a field in the table to be a link to that resource so that you don't have to scroll to the right to click on the Show icon. You can use link_to_resource to change a table cell to be a link to that resource.
# for id field
field :id, as: :id, link_to_resource: true
# for text field
field :name, as: :text, link_to_resource: true
# for gravatar field
field :email, as: :gravatar, link_to_resource: true
You can add this property on Id, Text, and Gravatar fields.
Optionally you can enable the global config id_links_to_resource. More on that on the id links to resource docs page.
Related:
Align text on Index view
It's customary on tables to align numbers to the right. You can do that using the index_text_align option. Valid values are :right or :center.
class ProjectResource < Avo::BaseResource
field :users_required, as: :number, index_text_align: :right
end
Stacked layout
For some fields, it might make more sense to use all of the horizontal area to display it. You can do that by changing the layout of the field wrapper using the stacked option.
field :meta, as: :key_value, stacked: trueinline layout (default)

stacked layout

Global stacked layout
You may also set all the fields to follow the stacked layout by changing the field_wrapper_layout initializer option from :inline (default) to :stacked.
Avo.configure do |config|
config.field_wrapper_layout = :stacked
endNow, all fields will have the stacked layout throughout your app.
Friendly.rb - Your friendly European Ruby Conference