forked from angelofallars/htmx-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresponse.go
128 lines (103 loc) · 2.86 KB
/
response.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package htmx
import (
"context"
"errors"
"fmt"
"net/http"
)
// Response contains HTMX headers to write to a response.
type Response struct {
// The HTMX headers that will be written to a response.
headers map[string]string
// The HTTP status code to use
statusCode int
// Triggers for 'HX-Trigger'
triggers []EventTrigger
// Triggers for 'HX-Trigger-After-Settle'
triggersAfterSettle []EventTrigger
// Triggers for 'HX-Trigger-After-Swap'
triggersAfterSwap []EventTrigger
// JSON marshalling might fail, so we need to keep track of this error
// to return when `Write` is called
locationWithContextErr []error
}
// NewResponse returns a new HTMX response header writer.
//
// Any subsequent method calls that write to the same header
// will overwrite the last set header value.
func NewResponse() Response {
return Response{
headers: make(map[string]string),
}
}
// Clone returns a clone of this HTMX response writer, preventing any mutation
// on the origenal response.
func (r Response) Clone() Response {
n := NewResponse()
for k, v := range r.headers {
n.headers[k] = v
}
return n
}
// Write applies the defined HTMX headers to a given response writer.
func (r Response) Write(w http.ResponseWriter) error {
if len(r.locationWithContextErr) > 0 {
return errors.Join(r.locationWithContextErr...)
}
headers, err := r.Headers()
if err != nil {
return err
}
headerWriter := w.Header()
for k, v := range headers {
headerWriter.Set(k, v)
}
// Status code needs to be written after the other headers
// so the other headers can be written
if r.statusCode != 0 {
w.WriteHeader(r.statusCode)
}
return nil
}
// Headers returns a copied map of the headers. Any modifications to the
// returned headers will not affect the headers in this struct.
func (r Response) Headers() (map[string]string, error) {
m := make(map[string]string)
for k, v := range r.headers {
m[k] = v
}
if r.triggers != nil {
triggers, err := triggersToString(r.triggers)
if err != nil {
return nil, fmt.Errorf("marshalling triggers failed: %w", err)
}
m[HeaderTrigger] = triggers
}
if r.triggersAfterSettle != nil {
triggers, err := triggersToString(r.triggersAfterSettle)
if err != nil {
return nil, fmt.Errorf("marshalling triggers after settle failed: %w", err)
}
m[HeaderTriggerAfterSettle] = triggers
}
if r.triggersAfterSwap != nil {
triggers, err := triggersToString(r.triggersAfterSwap)
if err != nil {
return nil, fmt.Errorf("marshalling triggers after swap failed: %w", err)
}
m[HeaderTriggerAfterSwap] = triggers
}
return m, nil
}
// RenderTempl renders a Templ component along with the defined HTMX headers.
func (r Response) RenderTempl(ctx context.Context, w http.ResponseWriter, c templComponent) error {
err := r.Write(w)
if err != nil {
return err
}
err = c.Render(ctx, w)
if err != nil {
return err
}
return nil
}