HTML Views
Overview
HTML Views are responsible for generating complete HTML pages by assembling layouts, sections, and components. They coordinate the presentation layer, bringing together data from Maestro operations and formatting it into structured HTML responses.
Purpose
HTML Views serve several key functions: - Page Assembly - Combine layouts, sections, and components into complete pages - Data Presentation - Transform Maestro output into HTML structures - Layout Management - Apply consistent page layouts across the application - Component Orchestration - Coordinate multiple UI components
Structure
Each HTML view typically follows this pattern:
type ListPlantTasksView struct {
Kit *kit.Kit
ListSection *sections.ListSection
NavbarSection *sections.NavbarSection
TiledLayout *layouts.TiledLayout
TitleSection *sections.TitleSection
}
func (this *ListPlantTasksView) H(out *maes.ListPlantTasksMaeOut) goc.HTML {
mod := &layouts.LayoutMod{
Messages: out.Extra.Messages,
Scope: out.Extra.Scope,
Ctx: out.Ctx,
}
return this.TiledLayout.H(this.tiling(out), mod)
}
Core Methods
Main Render Method
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
mod := &layouts.LayoutMod{
Messages: out.Extra.Messages,
Scope: out.Extra.Scope,
Ctx: out.Ctx,
Title: "Page Title",
}
return this.StructuredLayout.H(this.content(out), mod)
}
Content Assembly
func (this *ListPlantTasksView) tiling(out *maes.ListPlantTasksMaeOut) goc.HTML {
component := this.Kit.Component
return component.Dcs("flex flex-col space-y-4",
this.NavbarSection.H(out),
this.TitleSection.H(out),
this.ListSection.H(out),
this.footerSection(out),
)
}
Layout Integration
Layout Types
// Structured layout with sidebar
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
return this.StructuredLayout.H(content, mod)
}
// Tiled layout for flexible content
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
return this.TiledLayout.H(content, mod)
}
// Vertical layout for forms
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
return this.VerticalLayout.H(content, mod)
}
// Empty layout for minimal pages
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
return this.EmptyLayout.H(content, mod)
}
Layout Modification
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
mod := &layouts.LayoutMod{
Messages: out.Extra.Messages,
Scope: out.Extra.Scope,
Ctx: out.Ctx,
Title: "Custom Page Title",
MetaTitle: "SEO Title",
Description: "Page description for SEO",
Keywords: []string{"keyword1", "keyword2"},
BodyClasses: []string{"custom-page", "special-layout"},
}
return this.StructuredLayout.H(this.content(out), mod)
}
Section Integration
Common Sections
func (this *SomeView) content(out *maes.SomeMaeOut) goc.HTML {
return this.Kit.Component.Dcs("container mx-auto",
// Navigation
this.NavbarSection.H(out),
// Page title
this.TitleSection.H(out),
// Main content
this.mainContent(out),
// Actions
this.ActionSection.H(out),
)
}
Custom Sections
func (this *SomeView) customSection(out *maes.SomeMaeOut) goc.HTML {
component := this.Kit.Component
return component.Dcs("bg-gray-100 p-4 rounded",
component.H2("Custom Section"),
component.P("This is custom content specific to this view"),
this.customData(out.Data),
)
}
Component Usage
Form Components
func (this *CreatePlantView) formSection(out *maes.CreatePlantMaeOut) goc.HTML {
return this.Kit.Component.Form(
this.Kit.Component.FormField("text", "name", "Plant Name", out.Form.Name),
this.Kit.Component.FormField("text", "species", "Species", out.Form.Species),
this.Kit.Component.FormField("number", "size", "Size", out.Form.Size),
this.Kit.Component.FormField("checkbox", "perennial", "Perennial", out.Form.Perennial),
this.Kit.Component.SubmitButton("Create Plant"),
)
}
Table Components
func (this *ListPlantsView) tableSection(out *maes.ListPlantsMaeOut) goc.HTML {
var rows []goc.HTML
for _, plant := range out.Plants {
row := this.Kit.Component.TableRow(
this.Kit.Component.TableCell(plant.Name),
this.Kit.Component.TableCell(plant.Species),
this.Kit.Component.TableCell(fmt.Sprintf("%d", plant.Size)),
this.actionsCell(plant),
)
rows = append(rows, row)
}
headers := []string{"Name", "Species", "Size", "Actions"}
return this.Kit.Component.Table(headers, rows)
}
Navigation Components
func (this *SomeView) navigationSection(out *maes.SomeMaeOut) goc.HTML {
return this.Kit.Component.Nav(
this.Kit.Component.NavLink("/plants", "Plants"),
this.Kit.Component.NavLink("/gardens", "Gardens"),
this.Kit.Component.NavLink("/tasks", "Tasks"),
this.userMenu(out.User),
)
}
Data Handling
Error Display
func (this *SomeView) H(out *maes.SomeMaeOut) goc.HTML {
content := this.mainContent(out)
// Add error messages if present
if len(out.Extra.Messages.Errors) > 0 {
content = this.Kit.Component.Dcs("space-y-4",
this.errorMessages(out.Extra.Messages.Errors),
content,
)
}
return this.StructuredLayout.H(content, mod)
}
func (this *SomeView) errorMessages(errors []string) goc.HTML {
var errorElements []goc.HTML
for _, error := range errors {
errorElements = append(errorElements,
this.Kit.Component.Dcs("bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded",
this.Kit.Component.Text(error),
),
)
}
return this.Kit.Component.Dcs("space-y-2", errorElements...)
}
Success Messages
func (this *SomeView) successMessages(messages []string) goc.HTML {
var messageElements []goc.HTML
for _, message := range messages {
messageElements = append(messageElements,
this.Kit.Component.Dcs("bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded",
this.Kit.Component.Text(message),
),
)
}
return this.Kit.Component.Dcs("space-y-2", messageElements...)
}
Conditional Content
User-Based Content
func (this *SomeView) content(out *maes.SomeMaeOut) goc.HTML {
content := this.mainContent(out)
if out.User != nil && out.User.IsAdmin {
content = this.Kit.Component.Dcs("space-y-4",
content,
this.adminPanel(out),
)
}
return content
}
Data-Based Content
func (this *ListPlantsView) content(out *maes.ListPlantsMaeOut) goc.HTML {
if len(out.Plants) == 0 {
return this.emptyState()
}
return this.Kit.Component.Dcs("space-y-6",
this.TitleSection.H(out),
this.filtersSection(out),
this.plantsTable(out),
this.paginationSection(out),
)
}
func (this *ListPlantsView) emptyState() goc.HTML {
return this.Kit.Component.Dcs("text-center py-12",
this.Kit.Component.H2("No plants found"),
this.Kit.Component.P("Start by creating your first plant"),
this.Kit.Component.Link("/plants/new", "Create Plant"),
)
}
Integration with Controllers
HTML Views are typically used in controller methods:
func (gc *GardenController) List(c *gin.Context) {
input := &maes.ListGardensMaeIn{}
output, err := gc.ListGardensMae.Execute(c.Request.Context(), input)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
html := gc.ListGardensView.H(output)
c.Data(200, "text/html; charset=utf-8", []byte(html))
}
Best Practices
Performance
- Minimize component nesting depth
- Reuse components across views
- Avoid expensive operations in view methods
Maintainability
- Keep views focused on presentation logic
- Extract common patterns into reusable components
- Use consistent naming conventions
Responsive Design
- Use responsive CSS classes
- Test views across different screen sizes
- Implement mobile-first approaches
Accessibility
- Include proper ARIA labels
- Ensure keyboard navigation works
- Provide alt text for images
Testing HTML Views
func TestListPlantsView_H(t *testing.T) {
view := setupListPlantsView()
output := &maes.ListPlantsMaeOut{
Plants: []*mdl.Plant{
{Name: "Tomato", Species: "Solanum lycopersicum"},
{Name: "Basil", Species: "Ocimum basilicum"},
},
}
html := view.H(output)
assert.Contains(t, string(html), "Tomato")
assert.Contains(t, string(html), "Basil")
assert.Contains(t, string(html), "Solanum lycopersicum")
}
func TestCreatePlantView_ErrorHandling(t *testing.T) {
view := setupCreatePlantView()
output := &maes.CreatePlantMaeOut{
Extra: &maes.MaeExtra{
Messages: &maes.Messages{
Errors: []string{"Name is required", "Size must be positive"},
},
},
}
html := view.H(output)
assert.Contains(t, string(html), "Name is required")
assert.Contains(t, string(html), "Size must be positive")
}
HTML Views provide the final layer in the presentation stack, assembling all UI components into cohesive, functional web pages that deliver great user experiences.