Hello.
I’m trying to find a library or example for how to implement decorator pattern with a HTML template.
Here’s example with HTML form.
For example you have this kind of structure:
[
{
"name": "firstname",
"description": "First name",
"type": "input",
"req": true,
"placeholder": "Enter your first name",
"value": "John"
},
{
"name": "lastname",
"description": "Last name",
"type": "input",
"req": true,
"placeholder": "Enter your last name",
"value": "Doe"
},
{
"name": "age",
"description": "Age",
"type": "input",
"req": true,
"placeholder": "Enter your age",
"value": "27"
},
{
"name": "descr",
"description": "Description",
"type": "textarea",
"req": false,
"placeholder": "Enter description"
}
]
The base/default decorators are for types:
input:
<label for="{{ .Name }}">{{if .Required}}*{{end}}{{ .Description }}</label>
<input type="text" name="{{ .Name }}" id="{{ .Name }}" value="{{ .Value }}" placeholder="{{ .Placeholder }}" />
textarea:
<label for="{{ .Name }}">{{if .Required}}*{{end}}{{ .Description }}</label>
<textarea name="{{ .Name }}" id="{{ .Name }}" placeholder="{{ .Placeholder }}">{{ .Value }}</textarea>
And then there are the actual decorators:
div_example:
<div class="decorator{{if .Required}} required{{end}}">{{.}}</div>
And then there’s the most highest decorator that encloses the whole form:
<form action="{{.Action}}" method="{{.Method}}">
{{.}}
</form>
So the end result is for example:
<form action="foo" method="post">
<div class="decorator required">
<label for="firstname">*First name</label>
<input type="text" name="firstname" id="firstname" value="John" placeholder="Enter your first name" />
</div>
....
<div class="decorator">
<textarea name="descr" id="descr" placeholder="Enter description"></textarea>
</div>
</form>
Decorator chains can be changed, added or removed. So the output could also look like as that JSON in the beginning.
Here’s what I’ve got so far:
package main
import (
"encoding/json"
"fmt"
"html/template"
"os"
)
type FormFieldDescription struct {
Name string `json:"name,"`
Description string `json:"description,"`
Type string `json:"type,"`
Required bool `json:"req"`
Placeholder string `json:"placeholder,omitempty"`
Value string `json:"value,omitempty"`
}
const (
dec_input_text = `{{ define "default.field.input" }}
<label for="{{ .Name }}">{{if .Required}}*{{end}}{{ .Description }}</label>
<input type="text" name="{{ .Name }}" id="{{ .Name }}" value="{{ .Value }}" placeholder="{{ .Placeholder }}" />
{{ end }}`
dec_textarea = `{{ define "default.field.input" }}
<label for="{{ .Name }}">{{if .Required}}*{{end}}{{ .Description }}</label>
<textarea name="{{ .Name }}" id="{{ .Name }}" placeholder="{{ .Placeholder }}">{{ .Value }}</textarea>
{{ end }}`
dec_field_div = `{{define "default.field.decorator.div"}}<div class="decorator{{if .Required}} required{{end}}">
{{.}}
</div>
{{end}}`
)
func main() {
// Generated dynamically from struct
var fields []FormFieldDescription
fields = append(fields, FormFieldDescription{
Name: "firstname",
Description: "First name",
Placeholder: "Enter your first name",
Type: "input",
Required: true,
Value: "John",
})
fields = append(fields, FormFieldDescription{
Name: "lastname",
Description: "Last name",
Placeholder: "Enter your last name",
Type: "input",
Required: true,
Value: "Doe",
})
fields = append(fields, FormFieldDescription{
Name: "age",
Description: "Age",
Placeholder: "Enter your age",
Type: "input",
Required: true,
Value: "27",
})
fields = append(fields, FormFieldDescription{
Name: "descr",
Description: "Description",
Placeholder: "Enter description",
Type: "textarea",
Required: false,
Value: "",
})
var err error
jsonb, err := json.MarshalIndent(&fields, "", " ")
fmt.Printf("Generating from:\n%v\n\n", string(jsonb))
tpl := template.New("html")
if err != nil {
panic(err)
}
// Load decorators
tpl.Parse(dec_input_text)
tpl.Parse(dec_textarea)
tpl.Parse(dec_field_div)
decorators := []string{"default", "default.field.decorator.div" /* N decorators ... */}
fmt.Println("Into HTML:")
// Apply decorators
for _, item := range fields {
tmpWriter := os.Stdout
for _, decorator := range decorators {
if decorator == "default" {
decorator = fmt.Sprintf("default.field.%v", item.Type)
}
tpl.ExecuteTemplate(tmpWriter, decorator, item)
}
}
// Then apply most highest decorators, ie. <form>
}
I’m also unsure should the decorator pipeline chain flow be from base to highest decorator as it is now or vice versa.
I think chains also should be array of template functions as in
func input() Template {}
func div() Template {}
decoratorChain := [input, div]
Any pointers and help for how to implement this is welcome.