Golang中如何实现UI组件的复用
Golang中如何实现UI组件的复用
<div class="sm:col-span-4">
<label for="username"
class="block text-sm font-medium leading-6 text-gray-900">Username</label>
<div class="mt-2">
<div
class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<input type="text" name="username" id="username"
autocomplete="username"
class="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
placeholder="janesmith">
</div>
</div>
</div>
如果我将这个输入字段保存在一个文件中,该如何复用它? 不同的标签,不同的输入名称和ID?
我是否必须使用 .go 文件来提供所需的详细信息?
更多关于Golang中如何实现UI组件的复用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
哇。这真是太棒了。 您为模板命名的方式真是非同凡响。
非常感谢。我会以此为基础… 非常感谢您分享这份宝贵的内容。
更多关于Golang中如何实现UI组件的复用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢你们的支持。 你们两位都给予了我极大的帮助。
对于像我这样的人来说,从一个框架切换到另一个框架是很困难的。 你们的帮助给了我很多信心,让我能走在正确的道路上。
非常感谢。
Mahesh_Kumar1:
执行这些模板的 Go 文件现在既处理 UI,也处理 UI 所需的数据。这正常吗?
无论它是组件还是基础模板,向模板 {{.}} 传递数据都是正常的。你可以通过简单地使用句点将数据从基础模板传递给组件。
{{template "component" . }}
但我不确定这是否是正确的做法。
如果浏览器中的 HTML 显示正常,那么你就已经用“正确的方式”完成了。
我猜 html/template 这个包正是你所需要的。它允许你将 HTML 代码转换为带有占位符的模板文件。然后,你的 Go 代码就可以用数据来填充这些占位符。
整个过程在这里详细解释会过于冗长,但以下是一些帮助你入门的链接:
- Go by Example: 文本模板:关于模板的简明介绍
- 模板 - Go Web 示例:另一个入门介绍
- text/template 包的文档是所有模板功能的参考手册。(
html/template的工作方式相同,但内置了额外的、与 HTML 相关的安全功能。)
Mahesh_Kumar1:
从一个框架切换过来很困难
Go 不是一个框架。但使用模板时,它的行为在某种程度上就像一个框架。在我看来,它和 Angular 相差不远。以下是我的一个基础模板示例。通过复用组件使其更加 DRY(不重复自己)…
<!DOCTYPE html>
<html lang="en">
<head>
{{template "httphead"}}
</head>
<body data-theme="default" data-module="home" data-main="home">
{{template "icn"}} {{template "nav"}}
<main>
{{template "header" "Home"}}
<section>
{{template "lst_home" .}}{{template "desk" .}}
</section>
</main>
{{template "httpend"}}
</body>
</html>
我可以复制UI吗?似乎比在Go文件中维护UI更容易……正确的方法是什么?请给出建议。
我不确定我是否正确理解了你的问题。但正如 @christophberger 建议的,HTML模板也是我用来创建可重用“组件”的选择。请注意,Go本身没有内置的UI,因此你必须使用HTML和CSS来创建UI。
首先,你创建一个“组件”:
{{define "component"}} // 相应地命名组件
<p>This is a component</p>
{{end}}
然后,你可以在其他模板中任意多次地重用这个组件。
<h1>Here is the base template</h1>
{{template "component"}} // 这将调用HTML组件
...
<h1>Here is another template</h1>
{{template "component"}} // 这将调用相同的HTML组件
...
{{define "input-simple"}}
{{.}}
<div class="sm:col-span-4">
<label for="{{.Input}}"
class="block text-sm font-medium leading-6 text-gray-900">{{.Label}}</label>
<div class="mt-2">
<div
class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<input type="text" name="{{.Input}}" id="{{.Input}}"
class="block flex-1 border-0 bg-transparent py-1.5 pl-2 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
placeholder="{{.Placeholder}}" />
</div>
</div>
</div>
{{end}}
这就是我的实现方式。 但我不确定这是否正确。
尽管我复用了UI, 但现在执行此模板的Go文件既要处理UI,也要处理UI所需的数据。 这正常吗?
顺便提一下,另一种减少子模板(也称为“组件”)数量的方法是“嵌入”并将多个相关的子模板定义到一个单一模板中。
{{define "sub_home"}}
<div id="sub">
<ul id="sublist">
<li id="sub_home"><a href="/home">{{trans "Home"}}</a></li>
<li id="sub_gwd"><a href="https://go4webdev.org">Hub go4webdev</a></li>
</ul></div>
{{end}}
{{define "sub_howto"}}
<div id="sub">
<ul id="sublist">
<li id="sub_html"><a href="/html">{{trans "HTML"}}</a></li>
</ul>
</div>
{{end}}
{{define "sub_prefs"}}
<div id="sub">
<ul id="sublist">
<li id="sub_prefs"><a href="/prefs">{{trans "Personal"}}</a></li>
</ul>
</div>
{{end}}
<div class="sm:col-span-4">
<label for="{{.label}}"
class="block text-sm font-medium leading-6 text-gray-900">{{.name}}</label>
<div class="mt-2">
<div
class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<input type="text" name="{{.input_name}}" id="{{.input_name}}"
autocomplete="{{.name}}"
class="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
placeholder="{{.placeholder}}">
</div>
</div>
</div>
这看起来是一项非常庞大的工作。 Go 文件已经做了很多逻辑处理来为模板生成数据。 现在,我还需要维护那个文件来实现可复用的 UI。
这是唯一的解决方案吗? 或者, 我可以复制 UI 吗?这似乎比在 Go 文件中维护 UI 更容易维护… 正确的方法是什么?请给出建议。
我以前使用 React 做前端。这是我第一次使用 Go 做前端。我之前只用 Go 来构建 REST API。 它与 React 相比非常不同。
我打算完全放弃 React…
在Go中实现UI组件的复用通常需要使用模板系统。以下是几种实现方式:
1. 使用标准库的html/template
// components/input.go
package components
import (
"html/template"
"io"
)
type InputField struct {
Label string
Name string
ID string
Placeholder string
Value string
Class string
}
func (f InputField) Render(w io.Writer) error {
tmpl := `{{define "inputField"}}
<div class="sm:col-span-4">
<label for="{{.ID}}"
class="block text-sm font-medium leading-6 text-gray-900">{{.Label}}</label>
<div class="mt-2">
<div class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<input type="text"
name="{{.Name}}"
id="{{.ID}}"
autocomplete="{{.Name}}"
class="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6 {{.Class}}"
placeholder="{{.Placeholder}}"
value="{{.Value}}">
</div>
</div>
</div>
{{end}}`
t, err := template.New("input").Parse(tmpl)
if err != nil {
return err
}
return t.ExecuteTemplate(w, "inputField", f)
}
2. 使用预编译模板
// components/templates.go
package components
import (
"html/template"
"sync"
)
var (
inputTemplate *template.Template
once sync.Once
)
func getInputTemplate() *template.Template {
once.Do(func() {
inputTemplate = template.Must(template.New("inputField").Parse(`
<div class="sm:col-span-4">
<label for="{{.ID}}"
class="block text-sm font-medium leading-6 text-gray-900">{{.Label}}</label>
<div class="mt-2">
<div class="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<input type="text"
name="{{.Name}}"
id="{{.ID}}"
autocomplete="{{.Name}}"
class="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6 {{.Class}}"
placeholder="{{.Placeholder}}"
value="{{.Value}}">
</div>
</div>
</div>
`))
})
return inputTemplate
}
func RenderInput(field InputField) (string, error) {
var buf bytes.Buffer
if err := getInputTemplate().Execute(&buf, field); err != nil {
return "", err
}
return buf.String(), nil
}
3. 在Handler中使用
// main.go
package main
import (
"html/template"
"net/http"
"your-project/components"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 复用输入组件
usernameField := components.InputField{
Label: "Username",
Name: "username",
ID: "username",
Placeholder: "janesmith",
Value: "",
Class: "",
}
emailField := components.InputField{
Label: "Email",
Name: "email",
ID: "email",
Placeholder: "user@example.com",
Value: "",
Class: "",
}
// 使用模板渲染整个页面
tmpl := template.Must(template.ParseFiles("layout.html"))
data := struct {
UsernameField components.InputField
EmailField components.InputField
}{
UsernameField: usernameField,
EmailField: emailField,
}
tmpl.Execute(w, data)
})
http.ListenAndServe(":8080", nil)
}
4. 在模板文件中使用
<!-- layout.html -->
<!DOCTYPE html>
<html>
<body>
<form>
{{template "inputField" .UsernameField}}
{{template "inputField" .EmailField}}
<!-- 自定义样式的输入框 -->
{{$customField := .UsernameField}}
{{$customField.Class = "custom-class"}}
{{$customField.Label = "Custom Username"}}
{{template "inputField" $customField}}
</form>
</body>
</html>
5. 使用函数式选项模式增强灵活性
// components/input_advanced.go
package components
type InputOption func(*InputField)
func WithClass(class string) InputOption {
return func(f *InputField) {
f.Class = class
}
}
func WithValue(value string) InputOption {
return func(f *InputField) {
f.Value = value
}
}
func NewInputField(label, name, id, placeholder string, opts ...InputOption) InputField {
field := InputField{
Label: label,
Name: name,
ID: id,
Placeholder: placeholder,
}
for _, opt := range opts {
opt(&field)
}
return field
}
// 使用示例
username := NewInputField("Username", "username", "username", "janesmith",
WithClass("mb-4"),
WithValue("john_doe"),
)
这种方式允许你通过创建可配置的组件结构体来复用UI组件,每个实例可以有不同的标签、名称、ID和其他属性。

