Presenters
Overview
Presenters are responsible for transforming entity data into display-ready formats for views. They handle column definitions, data formatting, filtering, and search presentation, providing a clean separation between data models and view presentation logic.
Purpose
Presenters serve several key functions: - Data Formatting - Convert raw entity data into display formats - Column Management - Define and manage table/list column configurations - Filter Integration - Provide filter options for entity lists - Search Presentation - Format entities for search results - View Abstraction - Abstract presentation logic from controllers
Structure
Each presenter typically follows this pattern:
type PlantPresenter struct {
Kit *kit.Kit
PlantPrimaryColumnIllustrer *illustrers.PlantPrimaryColumnIllustrer
PlantSearchIllustrer *illustrers.PlantSearchIllustrer
Presenter *presenter.Presenter
}
Core Methods
Data Column Formatting
func (this *PlantPresenter) NameDataColumn(entity *mdl.Plant) goc.HTML {
return this.Kit.Atoms.Cells.TextCell.TextCell(entity.Name)
}
func (this *PlantPresenter) CreatedAtDataColumn(entity *mdl.Plant) goc.HTML {
return this.Kit.Atoms.Cells.TextCell.TextCell(entity.CreatedAt.Format("02/01/2006 15:04"))
}
func (this *PlantPresenter) HarvestedDataColumn(entity *mdl.Plant) goc.HTML {
return this.Kit.Atoms.Cells.DecimalCell.DecimalCell(entity.Harvested)
}
func (this *PlantPresenter) PerennialDataColumn(entity *mdl.Plant) goc.HTML {
return this.Kit.Atoms.Cells.BooleanCell.BooleanCell(entity.Perennial)
}
Dynamic Column Access
func (this *PlantPresenter) ColumnByKey(key string, entity *mdl.Plant) goc.HTML {
switch key {
case "name":
return this.NameDataColumn(entity)
case "created_at":
return this.CreatedAtDataColumn(entity)
case "harvested":
return this.HarvestedDataColumn(entity)
case "perennial":
return this.PerennialDataColumn(entity)
}
return this.Kit.Organisms.Tables.Table.EmptyCellRow()
}
Column Configuration
Column Definition
func (this *PlantPresenter) Columns(filter string) []*presenter.Column {
var columns []*presenter.Column
columns = append(columns, &presenter.Column{
Key: "name",
Label: "Name",
Kind: "data",
Main: true, // Show in main view
Sub: true, // Show in sub view
})
columns = append(columns, &presenter.Column{
Key: "created_at",
Label: "Created At",
Kind: "data",
Main: false, // Hide in main view
Sub: false, // Hide in sub view
})
return this.Presenter.FilteredColumns(columns, filter)
}
Column Types
- data - Standard data columns
- action - Action/button columns
- link - Link/navigation columns
- image - Image display columns
Search Integration
Search Presentation
func (this *PlantPresenter) Search(ctx context.Context, value *searcher.SearchValue, entity *mdl.Plant) goc.HTML {
return this.PlantSearchIllustrer.Act(ctx, &illustrers.PlantSearchIllustrerMod{
Entity: entity,
Value: value,
})
}
Primary Column Display
func (this *PlantPresenter) PrimaryColumn(ctx context.Context, entity *mdl.Plant) goc.HTML {
return this.PlantPrimaryColumnIllustrer.Act(ctx, &illustrers.PlantPrimaryColumnIllustrerMod{
Entity: entity,
})
}
Filter Configuration
func (this *PlantPresenter) Filters(ctx context.Context) []filter.Filter {
filters := filter.Filters{}
filters.AddIdFilter("id")
filters.AddStringFilter("name")
filters.AddStringFilter("exposition")
filters.AddBooleanFilter("perennial")
filters.AddIntegerFilter("size")
return filters
}
Integration with Views
Presenters are used by views to format data for display:
// In a view template
func (this *PlantListView) renderTable(plants []*mdl.Plant) goc.HTML {
var rows []goc.HTML
for _, plant := range plants {
row := this.Kit.Organisms.Tables.TableRow.TableRow(
this.PlantPresenter.NameDataColumn(plant),
this.PlantPresenter.PerennialDataColumn(plant),
this.PlantPresenter.HarvestedDataColumn(plant),
)
rows = append(rows, row)
}
return this.Kit.Organisms.Tables.Table.Table(rows...)
}
Cell Types
Text Cells
func (this *SomePresenter) TextColumn(entity *mdl.SomeEntity) goc.HTML {
return this.Kit.Atoms.Cells.TextCell.TextCell(entity.TextValue)
}
Numeric Cells
func (this *SomePresenter) IntegerColumn(entity *mdl.SomeEntity) goc.HTML {
return this.Kit.Atoms.Cells.IntegerCell.IntegerCell(entity.IntValue)
}
func (this *SomePresenter) DecimalColumn(entity *mdl.SomeEntity) goc.HTML {
return this.Kit.Atoms.Cells.DecimalCell.DecimalCell(entity.DecimalValue)
}
Boolean Cells
func (this *SomePresenter) BooleanColumn(entity *mdl.SomeEntity) goc.HTML {
return this.Kit.Atoms.Cells.BooleanCell.BooleanCell(entity.BoolValue)
}
Date/Time Cells
func (this *SomePresenter) DateColumn(entity *mdl.SomeEntity) goc.HTML {
formatted := entity.Date.Format("02/01/2006")
return this.Kit.Atoms.Cells.TextCell.TextCell(formatted)
}
func (this *SomePresenter) DateTimeColumn(entity *mdl.SomeEntity) goc.HTML {
formatted := entity.DateTime.Format("02/01/2006 15:04")
return this.Kit.Atoms.Cells.TextCell.TextCell(formatted)
}
Custom Formatting
Conditional Formatting
func (this *PlantPresenter) StatusColumn(entity *mdl.Plant) goc.HTML {
var cssClass string
var text string
if entity.Harvested > 0 {
cssClass = "text-green-600"
text = "Harvested"
} else {
cssClass = "text-yellow-600"
text = "Growing"
}
return this.Kit.Component.Dc(cssClass, this.Kit.Component.Text(text))
}
Complex Display Logic
func (this *PlantPresenter) SizeDisplayColumn(entity *mdl.Plant) goc.HTML {
switch {
case entity.Size < 10:
return this.Kit.Component.Text("Small")
case entity.Size < 50:
return this.Kit.Component.Text("Medium")
default:
return this.Kit.Component.Text("Large")
}
}
Best Practices
Performance
- Use appropriate cell types for data formatting
- Avoid complex calculations in presentation methods
- Cache formatted values when possible
Maintainability
- Keep presentation logic separate from business logic
- Use consistent naming conventions for column methods
- Group related columns logically
Flexibility
- Support different column filter sets
- Allow for conditional column display
- Provide fallbacks for missing data
Testing
func TestPlantPresenter_NameDataColumn(t *testing.T) {
presenter := setupPlantPresenter()
plant := &mdl.Plant{Name: "Tomato"}
result := presenter.NameDataColumn(plant)
assert.Contains(t, string(result), "Tomato")
}
func TestPlantPresenter_Columns(t *testing.T) {
presenter := setupPlantPresenter()
columns := presenter.Columns("")
assert.Greater(t, len(columns), 0)
assert.Contains(t, getColumnKeys(columns), "name")
}
Presenters provide a clean, consistent way to transform entity data into formatted display elements while maintaining separation of concerns between data models and view presentation.