Components
Lato provides a set of reusable functional components.
By using Lato components, you can save development time and simplify maintenance tasks.
All components are available via the Lato::ComponentsHelper helper.
Source code: Lato::ComponentsHelper
Navbar
Navbar nav item
Component to render a menu item in the navbar.
Parameters:
- key: the menu item's key. Used to activate the item via the active_navbar function from the Lato::Layoutable concern.
- path: a path or absolute URL to redirect the user.
- &block: block of HTML to render inside the menu item.
<%= lato_navbar_nav_item :account, lato.account_path do %> Account <% end %>
Sidebar
Sidebar nav item
Component to render a menu item in the sidebar.
Parameters:
- key: the menu item's key. Used to activate the item via the active_sidebar function from the Lato::Layoutable concern.
- path: a path or absolute URL to redirect the user.
- &block: block of HTML to render inside the menu item.
There are also some extra parameters to customize the menu item:
- external (default: false): if true, an icon is added to indicate that the link opens in a new tab and a target="_blank" attribute is added to the link.
- children (default: nil): an array of hashes representing submenu items. Each hash should contain the keys :key, :label, and :path. If this parameter is provided, the menu item will have a collapsible submenu.
<%= lato_sidebar_nav_item :account, lato.account_path do %>
Account
<% end %>
<%= lato_sidebar_nav_item :github, 'https://github.com/Lato-org/lato', external: true do %>
GitHub
<% end %>
<%= lato_sidebar_nav_item :examples, '#', children: [
{ key: :products, label: 'CRUD Example', path: main_app.products_path },
{ key: :other, label: 'Other Examples soon..', path: '#' },
] do %>
Examples
<% end %>
Page
Page head
<%= lato_page_head 'Page title' %>
Page title
<%= lato_page_head 'Page title', [{ label: 'Title 1', path: main_app.page_path }, { label: 'Title 2' }] %>
Page title
<%= lato_page_head 'Page title' do %> <p class="lead">Welcome to this page</p> <% end %>
Page title
Welcome to this page
Form
Forms can be generated using the lato_form components.
lato_form components are a set of utilities designed to work with Rails' form_with helper and the Stimulus controller lato_form_controller.
Below is a list of available components with usage examples:
-
lato_form_noticesdisplays success feedback (notices set by the controller action). -
lato_form_errorsdisplays error feedback (based on model instance errors). -
lato_form_item_labelrenders the field label. -
lato_form_item_input_textrenders a text input. -
lato_form_item_input_numberrenders a number input. -
lato_form_item_input_emailrenders an email input. -
lato_form_item_input_passwordrenders a password input. -
lato_form_item_input_checkrenders a checkbox input. -
lato_form_item_input_selectrenders a select input. -
lato_form_item_input_filerenders a file input. -
lato_form_item_input_file_dropzonerenders a file input with drag-and-drop support. -
lato_form_item_input_textarearenders a textarea input. -
lato_form_item_input_daterenders a date input. -
lato_form_item_input_datetimerenders a datetime input. -
lato_form_item_input_timerenders a time input. -
lato_form_item_input_colorrenders a color input. -
lato_form_submitrenders the submit button.
<%
user ||= @session.user
%>
<%= turbo_frame_tag 'tutorial_form-example' do %>
<%= form_with model: user, url: main_app.components_update_user_action_path, data: {
turbo_frame: '_self',
controller: 'lato-form'
} do |form| %>
<%= lato_form_notices class: %w[mb-3] %>
<%= lato_form_errors user, class: %w[mb-3] %>
<div class="row">
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :first_name, 'Name' %>
<%= lato_form_item_input_text form, :first_name, required: true %>
</div>
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :last_name, 'Surname' %>
<%= lato_form_item_input_text form, :last_name, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :select, 'Select' %>
<%= lato_form_item_input_select form, :select, [['Type 1', '1'], ['Type 2', '2']], required: true %>
</div>
<div class="col col-12 col-lg-6 mb-3">
<%= lato_form_item_label form, :file, 'File' %>
<%= lato_form_item_input_file form, :file, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :textarea, 'Textarea' %>
<%= lato_form_item_input_textarea form, :textarea, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 col-lg-4 mb-3">
<%= lato_form_item_label form, :date, 'Date' %>
<%= lato_form_item_input_date form, :date, required: true %>
</div>
<div class="col col-12 col-lg-4 mb-3">
<%= lato_form_item_label form, :datetime, 'Datetime' %>
<%= lato_form_item_input_datetime form, :datetime, required: true %>
</div>
<div class="col col-12 col-lg-4 mb-3">
<%= lato_form_item_label form, :time, 'Time' %>
<%= lato_form_item_input_time form, :time, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :color, 'Color' %>
<%= lato_form_item_input_color form, :color, required: true %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :first_name, 'Name with auto-complete' %>
<%= lato_form_item_input_text form, :first_name, required: true, data: {
controller: 'lato-input-autocomplete',
lato_input_autocomplete_path_value: products_autocomplete_path
} %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :first_name, 'Name with auto-complete V2' %>
<%= lato_form_item_input_text form, :first_name, required: true, data: {
controller: 'lato-input-autocomplete2',
lato_input_autocomplete2_path_value: products_autocomplete_path
} %>
</div>
</div>
<div class="row">
<div class="col col-12 mb-3">
<%= lato_form_item_label form, :file_dropzone, 'File Dropzone' %>
<%= lato_form_item_input_file_dropzone form, :file_dropzone, required: true %>
</div>
</div>
<div class="d-flex justify-content-end">
<%= lato_form_submit form, 'Update', class: %w[btn-success] %>
</div>
<% end %>
<% end %>
Nested attributes
Lato provides support for nested attributes in forms using the lato_form_item_input_list component.
This component allows you to easily manage collections of associated records within a parent form.
The lato_form_item_input_list component requires three parameters:
- form: the form builder object.
- key: the attribute name representing the collection of associated records.
- partial_path: the path to the partial that will be used to render each item in the collection.
- partial_params (optional): a hash of additional parameters to pass to the partial.
The lato_form_item_input_list component will render the list of items associated to the form object record and a button to add new items.
Each item will be rendered using the specified partial. The partial will receive the following local variables:
- form: the form builder object for the item.
- *partial_params: any additional parameters passed to the component.
The partial used to render each item should contain only the fields related to the item.
Other hidden fields (such as the item ID) and the remove button are automatically handled by the component.
<%= lato_form_item_input_list(form, :product_items, 'products/input_list_product_item') %>
Example: main form
Example: form item partial
Example: controller params
Example: model configuration
Data
Data badge
The lato_data_badge component allows easy rendering of badges with different styles.
<%= lato_data_badge "badge primary", "primary" %> <%= lato_data_badge "badge secondary", "secondary" %> <%= lato_data_badge "badge info", "info" %> <%= lato_data_badge "badge success", "success" %> <%= lato_data_badge "badge warning", "warning" %> <%= lato_data_badge "badge danger", "danger" %> <%= lato_data_badge "badge light", "light" %> <%= lato_data_badge "badge dark", "dark" %>
Data user
The lato_data_user component allows easy rendering of user information with avatar and full name.
<%= lato_data_user("Mr. Wolf") %>
<%= lato_data_user("Ms. White", "https://i.pravatar.cc/40?img=5") %>
Index
The lato_index component allows easy visualization of database records.
Index (front-end)
The simplest way to use the component is to pass it an ActiveRecord collection:
<%= lato_index Product.all.limit(5) %>
Customizing columns
Columns can be customized using the :columns parameter:
<%= lato_index Product.all.limit(5), columns: %i[id code status] %>
Notes
Column titles can be customized using Rails translation files.
Example:
en:
activerecord:
attributes:
product:
code: Code
status: Status
Customizing column content
You can customize column content by creating helper methods with the format model_column(record).
For example, to customize the output of the :lato_user_id column, define the following helper in ProductsHelper:
class ProductsHelper
def product_lato_user_id(product)
product.lato_user.full_name
end
end
Adding custom columns
To add custom columns, declare a method in the model that provides the value.
For example, after adding a lifetime method to the Product model, you can use the :lifetime column:
class Product < ApplicationRecord
def lifetime
Time.now - created_at
end
end
class ProductsHelper
def product_lifetime(product)
Time.at(product.lifetime).utc.strftime('%H h %M m')
end
end
<%= lato_index Product.all.limit(5), columns: %i[id code status lifetime] %>
Notes
Custom columns can also be translated using Rails translation files, just like database-backed columns.
To add columns that don't map to real attributes, use attr_accessor :column_name instead of defining a method in the model:
class Product < ApplicationRecord attr_accessor :lifetime end
Adding actions
<%= lato_index Product.all.limit(5), custom_actions: {
create: {
path: products_create_path,
icon: 'bi bi-plus',
aria_label: 'Create product',
title: 'Create product'
}
} %>
Adding in-page actions
<%= lato_index Product.all.limit(5), custom_actions: {
create: {
path: products_create_path,
icon: 'bi bi-plus',
data: { lato_action_target: 'trigger', turbo_frame: dom_id(Product.new, 'form') },
aria_label: 'Create product',
title: 'Create product'
}
} %>
Notes
The :create_turbo_frame option specifies the turbo-frame ID in the products_create_path page that should be inserted into the overlay.
Adding row-specific actions
Row-specific actions can be added through custom columns (as shown above).
These actions can also be opened in-page via overlays using data-lato-index-target="action" and data-turbo-frame="turbo-frame-ID".
class ProductsHelper
def product_actions(product)
content_tag(:div, class: 'btn-group btn-group-sm') do
concat link_to(
'Edit',
products_update_path(product),
class: 'btn btn-primary',
data: { lato_action_target: 'trigger', turbo_frame: dom_id(product, 'form') }
)
end
end
end
<%= lato_index Product.all.limit(5), columns: %i[id code status lifetime actions] %>
Index (back-end)
To make the index fully interactive, use the lato_index_collection function from the Lato::Componentable concern in the controller.
def index @products = lato_index_collection(Product.all, pagination: true) end
<%= lato_index @products %>
Customizing columns
def index
@products_columns = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status id]
)
end
<%= lato_index @products_columns %>
Customizing pagination size
Users can choose how many items to display per page using a selector enabled via the :pagination_options parameter in lato_index_collection.
def index
@products_pagination_options = lato_index_collection(
Product.all,
pagination: 10,
columns: %i[code status id],
key: 'products_pagination_options'
)
end
<%= lato_index @products_pagination_options, pagination_options: [10,20,50,100] %>
Making columns sortable
def index
@products_sortable_columns = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status lato_user_id],
sortable_columns: %i[code status lato_user_id]
)
end
<%= lato_index @products_sortable_columns %>
Notes
To customize sorting logic, define the lato_index_order scope inside the model.
Example:
class Product < ApplicationRecord
belongs_to :lato_user, class_name: 'Lato::User'
scope :lato_index_order, ->(column, order) do
return joins(:lato_user).order("lato_users.last_name #{order}, lato_users.first_name #{order}") if column == :lato_user_id
order("#{column} #{order}")
end
end
Notes
To set a default sorting column, use the default_sort_by option in the lato_index_collection method.
Example:
def index
@products_default_sort = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status lato_user_id],
sortable_columns: %i[code status lato_user_id],
default_sort_by: 'code|asc' # 'column|direction'
)
end
Making columns searchable
def index
@products_searchable_columns = lato_index_collection(
Product.all,
pagination: true,
columns: %i[code status lato_user_id],
sortable_columns: %i[code status lato_user_id],
searchable_columns: %i[code lato_user_id]
)
end
<%= lato_index @products_searchable_columns %>
Notes
To customize search logic, define the lato_index_search scope inside the model.
Example:
class Product < ApplicationRecord
belongs_to :lato_user, class_name: 'Lato::User'
scope :lato_index_search, ->(search) do
joins(:lato_user).where("
lower(code) LIKE :search OR
lower(lato_users.first_name) LIKE :search OR
lower(lato_users.last_name) LIKE :search
", search: "%#{search.downcase.strip}%")
end
end
Skip total count for large tables
For large tables, you can skip the total count to improve performance by setting the :skip_total_count option to true in the lato_index method.
This is useful when you don't need the total count of records, such as when displaying a large collection.
<%= lato_index @products_skip_total_count_columns, skip_total_count: true %>