简化交互代码
Api,InitApi
假设我们页面有一个表单,用户输入信息,点击提交后我们需要把信息存入数据库。
借助 form 组件的 Api 方法,可以这样做:
func init() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
input, _ := io.ReadAll(r.Body)
defer r.Body.Close()
m := map[string]any{}
json.Unmarsharl(input, &m)
name := m["name"]
email := m["email"]
// save the user into db ...
})
}
...
comp.Page().Body(
comp.Form().Api("/user").Body(
comp.InputText().Label("姓名").Name("name"),
comp.InputEmail().Label("邮箱").Name("email"),
),
)
显然这个代码非常啰嗦,amisgo 给 form 组件增加了一个新方法 Submit:
// Submit 设置表单提交后的回调逻辑,使用通用的 Data 类型处理表单数据
// 适用于需要灵活处理表单提交的场景
func (f form) Submit(callback func(model.Data) error) form
这样一来,用户代码可以简化成如下:
comp.Page().Body(
comp.Form().Body(
comp.InputText().Label("姓名").Name("name"),
comp.InputEmail().Label("邮箱").Name("email"),
).Submit(func(m model.Data) error {
name := m.Get("name")
email := m.Get("email")
// save name and email to db
// ...
return nil
}),
)
同样还有个 SubmitTo 方法,使用具体类型处理表单数据
func (f form) SubmitTo(receiver any, callback func(any) error) form
类似地,我们包装了 page 组件的 InitApi,增加 InitData 方法:
func (p page) InitData(getter func() (any, error)) page
这样,用户写页面获取当前时间的代码就简化成了:
comp.Page().
Title("标题").
Body("内容部分. 可以使用 \\${var} 获取变量。如: `\\$date`: ${date}").
InitData(getDate)
func getDate() (any, error) {
y, m, d := time.Now().Date()
mm := time.Now().UnixNano()
return map[string]string{"date": fmt.Sprintf("%d-%d-%d %d", y, m, d, mm)}, nil
}
Action 按钮
假设页面有两个编辑器,第一个用于输入 json,第二个是只读的,当点击按钮后,以第一个编辑器的内容作为输入,转换为 yaml,渲染到第二个编辑器中,怎么做呢?
直接借助 ajax 类型的行为按钮,可以这么写:
func init() {
http.HandleFunc("/convert", func(w http.ResponseWriter, r *http.Request) {
input, _ := io.ReadAll(r.Body)
defer r.Body.Close()
m := map[string]any{}
json.Unmarshal(input, &m)
// ...
output := "age: 27"
resp := model.Response{Msg: "转换成功", Data: model.Data{"output": output}} // 这里的key值必须是第二个编辑器的 name
data, _ := json.Marshal(resp)
w.Write(data)
})
}
func main() {
index := comp.Page().Body(
comp.Form().ColumnCount(2).Body(
comp.Editor().Language("json").Label("json").Name("input").Size("xxl"),
comp.Editor().Label("yaml").Label("yaml").Name("output").Size("xxl").ReadOnly(true),
).Actions(
comp.Action().Label("Convert").Level("primary").ActionType("ajax").Api(
model.Schema{
"url": "/convert",
"data": model.Schema{"input": "${input}"},
"responses": model.Schema{
"200": model.Schema{
"then": model.Schema{
"actionType": "setValue",
"args": model.Schema{
"value": "${response}",
},
},
},
},
},
),
...
显然,这也比较啰嗦,我们为行为按钮新增了 Transform 方法:
func (a action) Transform(src, dst, successMsg string, transfor func(input any) (any, error)) action
上面的代码就可以简化成:
comp.Page().Body(
comp.Form().ColumnCount(2).Body(
comp.Editor().Language("json").Label("json").Name("input").Size("xxl"),
comp.Editor().Label("yaml").Label("yaml").Name("output").Size("xxl").ReadOnly(true),
).Actions(
comp.Action().Label("Converrt").Level("primary").Transform("input", "output", "转换成功", func(input any) (any, error) {
// transform input json to yaml
output := "age: 27"
return output, nil
}),
实际上,我们还支持了多对多的 TransformMultiple 方法,可以实现从多个组件的输入值转换后渲染到多个组件。
可以参考 dev-toys 中的示例,其中的 convert 组件用了 Transform, 生成多种类型的 hash 值用了 TransformMultiple。