Skip to content
This repository was archived by the owner on Aug 1, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type HTTPDecoder struct {
validationStore ValidationStore
setValueOnValidationError bool
numValidationErrors int
elementNamePrefix string
}

// NewDecoder creates a new HTTPDecoder.
Expand All @@ -40,6 +41,10 @@ func (h *HTTPDecoder) SetValidationStore(v ValidationStore) {
h.validationStore = v
}

func (h *HTTPDecoder) SetElementNamePrefix(prefix string) {
h.elementNamePrefix = prefix
}

// SetValueOnValidationError indicates whether a form value should be set in the form if there was a validation error on that value.
func (h *HTTPDecoder) SetValueOnValidationError(b bool) {
h.setValueOnValidationError = b
Expand Down Expand Up @@ -122,7 +127,7 @@ func (h *HTTPDecoder) Decode(data interface{}) error {
}

func (h *HTTPDecoder) getFormValues(key string) []string {
key = FormElementName(key)
key = FormElementName(h.elementNamePrefix, key)

var vals []string

Expand All @@ -137,7 +142,7 @@ func (h *HTTPDecoder) decode(val reflect.Value, key string, validators []Validat
if val.CanInterface() {
switch a := val.Interface().(type) {
case CustomDecoder:
decodedFormVal, err := a.DecodeFormValue(h.form, key, h.getFormValues(key))
decodedFormVal, err := a.DecodeFormValue(h.form, FormElementName(h.elementNamePrefix, key), h.getFormValues(key))

if err != nil {
return err
Expand All @@ -162,7 +167,7 @@ func (h *HTTPDecoder) decode(val reflect.Value, key string, validators []Validat

return nil
case time.Time:
formValue, ok := PopFormValue(h.form, FormElementName(key))
formValue, ok := PopFormValue(h.form, FormElementName(h.elementNamePrefix, key))

var t time.Time

Expand Down Expand Up @@ -230,7 +235,7 @@ func (h *HTTPDecoder) decode(val reflect.Value, key string, validators []Validat
return nil
}

formValue, ok := PopFormValue(h.form, FormElementName(key))
formValue, ok := PopFormValue(h.form, FormElementName(h.elementNamePrefix, key))

if !ok {
// below we are dealing with concrete types that do not call decode recursively.
Expand Down Expand Up @@ -358,7 +363,7 @@ func (h *HTTPDecoder) passedValidation(key string, value interface{}, validators
if !valid {
h.numValidationErrors++

err := h.validationStore.AddValidationError(FormElementName(key), ValidationError{
err := h.validationStore.AddValidationError(FormElementName(h.elementNamePrefix, key), ValidationError{
Value: value,
Error: message,
})
Expand Down
29 changes: 20 additions & 9 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ type HTMLEncoder struct {
w io.Writer
r *http.Request

decorator Decorator
format bool
validationStore ValidationStore
decorator Decorator
format bool
validationStore ValidationStore
elementNamePrefix string

csrfProtection bool
}
Expand Down Expand Up @@ -69,6 +70,11 @@ func (h *HTMLEncoder) SetCSRFProtection(enabled bool) {
h.csrfProtection = enabled
}

// SetElementNamePrefix allows a prefix to be specified for all element names.
func (h *HTMLEncoder) SetElementNamePrefix(prefix string) {
h.elementNamePrefix = prefix
}

// SetValidationStore can be used to tell the HTMLEncoder about previous validation errors.
func (h *HTMLEncoder) SetValidationStore(v ValidationStore) {
if v == nil {
Expand All @@ -78,6 +84,11 @@ func (h *HTMLEncoder) SetValidationStore(v ValidationStore) {
h.validationStore = v
}

// GetValidationStore returns the current validation store.
func (h *HTMLEncoder) GetValidationStore() ValidationStore {
return h.validationStore
}

func errorIncorrectValue(t reflect.Type) error {
return fmt.Errorf("formulate: encode expects a struct value, got: %s", t.String())
}
Expand Down Expand Up @@ -146,7 +157,7 @@ func (h *HTMLEncoder) recurse(v reflect.Value, key string, field StructField, pa
if v.CanInterface() {
switch v.Interface().(type) {
case time.Time, Select, RadioList, CustomEncoder:
return BuildField(v, FormElementName(key), field, parent, h.decorator, h.ShowConditions)
return BuildField(v, FormElementName(h.elementNamePrefix, key), field, parent, h.decorator, h.ShowConditions)
}
}

Expand All @@ -171,7 +182,7 @@ func (h *HTMLEncoder) recurse(v reflect.Value, key string, field StructField, pa

nextKey := key + fieldSeparator + v.Type().Field(i).Name

validationErrors, err := h.validationStore.GetValidationErrors(FormElementName(nextKey))
validationErrors, err := h.validationStore.GetValidationErrors(FormElementName(h.elementNamePrefix, nextKey))

if err != nil {
return err
Expand Down Expand Up @@ -217,7 +228,7 @@ func (h *HTMLEncoder) recurse(v reflect.Value, key string, field StructField, pa

return h.recurse(reflect.ValueOf(Raw(buf.Bytes())), key, field, parent)
default:
return BuildField(v, FormElementName(key), field, parent, h.decorator, h.ShowConditions)
return BuildField(v, FormElementName(h.elementNamePrefix, key), field, parent, h.decorator, h.ShowConditions)
}
}

Expand Down Expand Up @@ -787,14 +798,14 @@ func BuildRadioButtons(r RadioList, key string, field StructField, decorator Dec
const fieldSeparator = "."

// FormElementName returns the name of the form element within the form, removing the package path and base struct name.
func FormElementName(key string) string {
func FormElementName(prefix string, key string) string {
keySplit := strings.Split(key, fieldSeparator)

if len(keySplit) > 2 {
return strings.Join(keySplit[2:], fieldSeparator)
return prefix + strings.Join(keySplit[2:], fieldSeparator)
}

return key
return prefix + key
}

func BuildLabel(label string, parent *html.Node, field StructField, decorator Decorator) {
Expand Down
1 change: 1 addition & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func TestHtmlEncoder_Encode(t *testing.T) {
buf := new(bytes.Buffer)
m := NewEncoder(buf, nil, nil)
m.SetFormat(true)
m.SetElementNamePrefix("EncoderPrefix")

if err := m.Encode(&YourDetails{
Name: "Jane Doe",
Expand Down
2 changes: 1 addition & 1 deletion types.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ type BoolNumber int

// DecodeFormValue implements the CustomDecoder interface.
func (bn BoolNumber) DecodeFormValue(form url.Values, name string, _ []string) (reflect.Value, error) {
val, _ := PopFormValue(form, FormElementName(name))
val, _ := PopFormValue(form, name)

if val == "on" || val == "1" {
return reflect.ValueOf(BoolNumber(1)), nil
Expand Down