Inline Click To Edit
List view of items with a click to edit button and persistence
Name | Date of Birth | Role | Salary | |
---|---|---|---|---|
John Doe | 24/05/1995 | htmgo developer | $250,000 | |
Jake Smith | 24/05/1995 | htmx developer | $100,000 |
package snippets
import (
"fmt"
"github.com/maddalax/htmgo/framework/h"
)
// RowClasses defined here for simplicity of the example
var RowClasses = "whitespace-nowrap px-4 py-4 font-medium text-gray-900 text-left"
var ButtonClasses = "inline-block rounded bg-slate-900 px-4 py-2 text-xs font-medium text-white hover:bg-slate-800"
var InputClasses = "-ml-2 max-w-[125px] border p-2 rounded focus:outline-none focus:ring focus:ring-slate-800"
type Record struct {
Id string
Name string
Birthday string
Role string
Salary string
}
var records = []Record{
{
Id: "1",
Name: "John Doe",
Birthday: "24/05/1995",
Role: "htmgo developer",
Salary: "$250,000",
},
{
Id: "2",
Name: "Jake Smith",
Birthday: "24/05/1995",
Role: "htmx developer",
Salary: "$100,000",
},
}
func ClickToEdit(ctx *h.RequestContext) *h.Partial {
return h.NewPartial(
h.Div(
h.Class("flex gap-2 items-center w-full"),
Table(),
),
)
}
// StartEditing is a partial that is called when the user clicks on the edit button,
// it will swap in the form for editing for the given record
func StartEditing(ctx *h.RequestContext) *h.Partial {
id := ctx.QueryParam("id")
record := h.Find(records, func(record *Record) bool {
return record.Id == id
})
if record == nil {
return h.EmptyPartial()
}
return h.SwapManyPartial(
ctx,
TableRow(record, true),
)
}
// SaveEditing is a partial that is called when the user clicks on the save button while editing,
// it will update the record with the new values and swap it back out
// note: in the example, we are just creating a new record in memory instead of updating the existing one,
// normally you would update a persistent record in a database
func SaveEditing(ctx *h.RequestContext) *h.Partial {
id := ctx.QueryParam("id")
// just for the example, create a new record so it doesn't affect the global original
record := Record{
Id: id,
Name: ctx.FormValue("name"),
Birthday: ctx.FormValue("birthday"),
Role: ctx.FormValue("role"),
Salary: ctx.FormValue("salary"),
}
return h.SwapPartial(ctx, TableRow(&record, false))
}
func Table() *h.Element {
return h.Div(
h.Class("overflow-x-auto w-full"),
h.Table(
h.Class("divide-y divide-gray-200 bg-white table-fixed"),
h.THead(
h.Tr(
h.Th(
h.Class(RowClasses),
h.Text("Name"),
),
h.Th(
h.Class(RowClasses),
h.Text("Date of Birth"),
),
h.Th(
h.Class(RowClasses),
h.Text("Role"),
),
h.Th(
h.Class(RowClasses),
h.Text("Salary"),
),
h.Th(
h.Class("px-4 py-2"),
),
),
),
h.TBody(
h.Class("divide-y divide-gray-200"),
h.List(records, func(record Record, index int) *h.Element {
return TableRow(&record, false)
}),
),
),
)
}
func TableRow(record *Record, editing bool) *h.Element {
recordId := fmt.Sprintf("record-%s", record.Id)
var Cell = func(name string, value string) *h.Element {
return h.Td(
h.Class(RowClasses, "h-[75px]"),
h.IfElse(
!editing,
h.Pf(
value,
h.Class("w-[125px]"),
),
h.Input(
"text",
h.Class(InputClasses),
h.Value(value),
h.Name(name),
),
),
)
}
return h.Tr(
h.If(
editing,
// this is important to make sure the inputs are included in the form submission
h.HxInclude("input"),
),
h.Id(recordId),
Cell("name", record.Name),
Cell("birthday", record.Birthday),
Cell("role", record.Role),
Cell("salary", record.Salary),
// Edit button
h.Td(
h.Button(
h.Class(ButtonClasses),
h.PostPartialWithQs(
h.Ternary(!editing, StartEditing, SaveEditing),
h.NewQs("id", record.Id),
),
h.Text(
h.Ternary(!editing, "Edit", "Save"),
),
),
),
)
}
Copy
1package snippets
2
3import (
4 "fmt"
5 "github.com/maddalax/htmgo/framework/h"
6)
7
8// RowClasses defined here for simplicity of the example
9var RowClasses = "whitespace-nowrap px-4 py-4 font-medium text-gray-900 text-left"
10var ButtonClasses = "inline-block rounded bg-slate-900 px-4 py-2 text-xs font-medium text-white hover:bg-slate-800"
11var InputClasses = "-ml-2 max-w-[125px] border p-2 rounded focus:outline-none focus:ring focus:ring-slate-800"
12
13type Record struct {
14 Id string
15 Name string
16 Birthday string
17 Role string
18 Salary string
19}
20
21var records = []Record{
22 {
23 Id: "1",
24 Name: "John Doe",
25 Birthday: "24/05/1995",
26 Role: "htmgo developer",
27 Salary: "$250,000",
28 },
29 {
30 Id: "2",
31 Name: "Jake Smith",
32 Birthday: "24/05/1995",
33 Role: "htmx developer",
34 Salary: "$100,000",
35 },
36}
37
38func ClickToEdit(ctx *h.RequestContext) *h.Partial {
39 return h.NewPartial(
40 h.Div(
41 h.Class("flex gap-2 items-center w-full"),
42 Table(),
43 ),
44 )
45}
46
47// StartEditing is a partial that is called when the user clicks on the edit button,
48// it will swap in the form for editing for the given record
49func StartEditing(ctx *h.RequestContext) *h.Partial {
50 id := ctx.QueryParam("id")
51
52 record := h.Find(records, func(record *Record) bool {
53 return record.Id == id
54 })
55
56 if record == nil {
57 return h.EmptyPartial()
58 }
59
60 return h.SwapManyPartial(
61 ctx,
62 TableRow(record, true),
63 )
64}
65
66// SaveEditing is a partial that is called when the user clicks on the save button while editing,
67// it will update the record with the new values and swap it back out
68// note: in the example, we are just creating a new record in memory instead of updating the existing one,
69// normally you would update a persistent record in a database
70func SaveEditing(ctx *h.RequestContext) *h.Partial {
71 id := ctx.QueryParam("id")
72
73 // just for the example, create a new record so it doesn't affect the global original
74 record := Record{
75 Id: id,
76 Name: ctx.FormValue("name"),
77 Birthday: ctx.FormValue("birthday"),
78 Role: ctx.FormValue("role"),
79 Salary: ctx.FormValue("salary"),
80 }
81
82 return h.SwapPartial(ctx, TableRow(&record, false))
83}
84
85func Table() *h.Element {
86 return h.Div(
87 h.Class("overflow-x-auto w-full"),
88 h.Table(
89 h.Class("divide-y divide-gray-200 bg-white table-fixed"),
90 h.THead(
91 h.Tr(
92 h.Th(
93 h.Class(RowClasses),
94 h.Text("Name"),
95 ),
96 h.Th(
97 h.Class(RowClasses),
98 h.Text("Date of Birth"),
99 ),
100 h.Th(
101 h.Class(RowClasses),
102 h.Text("Role"),
103 ),
104 h.Th(
105 h.Class(RowClasses),
106 h.Text("Salary"),
107 ),
108 h.Th(
109 h.Class("px-4 py-2"),
110 ),
111 ),
112 ),
113 h.TBody(
114 h.Class("divide-y divide-gray-200"),
115 h.List(records, func(record Record, index int) *h.Element {
116 return TableRow(&record, false)
117 }),
118 ),
119 ),
120 )
121}
122
123func TableRow(record *Record, editing bool) *h.Element {
124 recordId := fmt.Sprintf("record-%s", record.Id)
125
126 var Cell = func(name string, value string) *h.Element {
127 return h.Td(
128 h.Class(RowClasses, "h-[75px]"),
129 h.IfElse(
130 !editing,
131 h.Pf(
132 value,
133 h.Class("w-[125px]"),
134 ),
135 h.Input(
136 "text",
137 h.Class(InputClasses),
138 h.Value(value),
139 h.Name(name),
140 ),
141 ),
142 )
143 }
144
145 return h.Tr(
146 h.If(
147 editing,
148 // this is important to make sure the inputs are included in the form submission
149 h.HxInclude("input"),
150 ),
151 h.Id(recordId),
152 Cell("name", record.Name),
153 Cell("birthday", record.Birthday),
154 Cell("role", record.Role),
155 Cell("salary", record.Salary),
156 // Edit button
157 h.Td(
158 h.Button(
159 h.Class(ButtonClasses),
160 h.PostPartialWithQs(
161 h.Ternary(!editing, StartEditing, SaveEditing),
162 h.NewQs("id", record.Id),
163 ),
164 h.Text(
165 h.Ternary(!editing, "Edit", "Save"),
166 ),
167 ),
168 ),
169 )
170}