LiveTable Generator
View SourceThe mix live_table.gen.live task generates Phoenix LiveView files with LiveTable integration, providing a quick way to scaffold CRUD interfaces with advanced table features.
Overview
This generator wraps mix phx.gen.live and enhances the generated index LiveView with:
- LiveTable.LiveResource - Adds the
use LiveTable.LiveResourcedeclaration - fields/0 function - Auto-generates field definitions based on schema fields
- filters/0 function - Auto-generates filters based on field types
- Template replacement - Replaces
<.table>with<.live_table>component
Usage
mix live_table.gen.live Context Schema table_name field:type field:type ...
Example
mix live_table.gen.live Accounts User users name:string email:string age:integer active:boolean
This generates:
- All standard
phx.gen.livefiles (context, schema, migrations, LiveViews, templates) - Enhanced index LiveView with LiveTable integration
- Automatic field definitions with appropriate options
- Smart filters based on field types
Generated Fields
The generator analyzes field types and creates appropriate field configurations:
String Fields
name: %{label: "Name", sortable: true, searchable: true}- sortable: Enables column sorting
- searchable: Enables text search
Text Fields
description: %{label: "Description", searchable: true}- searchable: Enables text search (no sorting due to length)
Numeric Fields (integer, float, decimal)
age: %{label: "Age", sortable: true}- sortable: Enables column sorting
Boolean Fields
active: %{label: "Active"}- Basic field without sorting/searching
ID Fields
id: %{label: "ID", sortable: true}- sortable: Enables column sorting
Generated Filters
The generator creates appropriate filters based on field types:
Boolean Filters
For boolean fields like active:boolean:
active_filter: Boolean.new(:active, "active_filter", %{
label: "Active",
condition: dynamic([r], r.active == true)
})Range Filters
For numeric fields like age:integer, price:decimal:
age_range: Range.new(:age, "age_range", %{
type: :number,
label: "Age Range",
min: 0,
max: 1000
})Excluded Fields
The following fields are automatically excluded from filters:
idinserted_atupdated_at
Empty Filters
If no filterable fields are present, an empty filters function is generated with a comment:
def filters do
[
# Add custom filters here
]
endGenerated Index LiveView
Before (phx.gen.live)
defmodule MyAppWeb.UserLive.Index do
use MyAppWeb, :live_view
alias MyApp.Accounts
alias MyApp.Accounts.User
# ... mount, handle_params, etc.
endAfter (live_table.gen.live)
defmodule MyAppWeb.UserLive.Index do
use MyAppWeb, :live_view
use LiveTable.LiveResource, schema: MyApp.Accounts.User
@impl LiveTable.LiveResource
def fields do
[
name: %{label: "Name", sortable: true, searchable: true},
email: %{label: "Email", sortable: true, searchable: true},
age: %{label: "Age", sortable: true},
active: %{label: "Active"}
]
end
@impl LiveTable.LiveResource
def filters do
[
age_range: Range.new(:age, "age_range", %{
type: :number,
label: "Age Range",
min: 0,
max: 1000
}),
active_filter: Boolean.new(:active, "active_filter", %{
label: "Active",
condition: dynamic([r], r.active == true)
})
]
end
alias MyApp.Accounts
alias MyApp.Accounts.User
# ... mount, handle_params, etc.
endGenerated Template
Before (phx.gen.live)
<.table
id="users"
rows={@streams.users}
row_click={fn {_id, user} -> JS.navigate(~p"/users/#{user}") end}
>
<:col :let={{_id, user}} label="Name">{user.name}</:col>
<:col :let={{_id, user}} label="Email">{user.email}</:col>
<:action :let={{_id, user}}>
<.link navigate={~p"/users/#{user}"}>Show</.link>
</:action>
</.table>After (live_table.gen.live)
<.live_table
fields={fields()}
filters={filters()}
options={@options}
streams={@streams}
/>Features
Automatic Field Inference
- Analyzes field types from command arguments
- Sets appropriate options (sortable, searchable)
- Humanizes field names for labels
Smart Filter Generation
- Creates boolean filters for boolean fields
- Creates range filters for numeric fields
- Skips timestamp and ID fields
- Handles cases with no filterable fields
Template Enhancement
- Replaces standard table with LiveTable component
- Removes column definitions (handled by fields/0)
- Removes action slots (handled by LiveTable)
- Maintains streams-based rendering
Non-Destructive
- Only modifies the index LiveView and template
- Leaves show, form, and other files untouched
- Preserves all phx.gen.live functionality
Advanced Customization
After generation, you can customize:
Field Options
def fields do
[
name: %{
label: "Full Name",
sortable: true,
searchable: true,
render: fn user ->
"#{user.first_name} #{user.last_name}"
end
}
]
endFilter Options
def filters do
[
price_range: Range.new(:price, "price_range", %{
type: :number,
label: "Price Range",
min: 0,
max: 10000, # Adjust max value
step: 100 # Add step value
}),
# Add custom filters
status: Select.new(:status, "status", %{
label: "Status",
options: [
%{label: "Active", value: ["active"]},
%{label: "Inactive", value: ["inactive"]}
]
})
]
endTable Options
def table_options do
%{
page_size: 20,
export_enabled: true,
view_mode: :table
}
endTesting
The generator is fully tested with comprehensive test coverage:
- Field generation for all types
- Filter generation for boolean and numeric types
- Template replacement
- Error handling
- File preservation
Limitations
Single Schema Only: Works with single-schema queries. For complex queries with joins, use manual LiveTable setup.
Basic Filters: Generated filters use default settings. Customize min/max values and other options as needed.
No Custom Queries: Generated code uses the schema directly. For custom queries, you'll need to manually set up a data provider.
Next Steps
After running the generator:
Run migrations:
mix ecto.migrateCustomize fields and filters as needed
Add table options if you want exports, custom page sizes, etc.
Test your LiveTable by starting the server and navigating to the generated routes