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

10 回复

哇。这真是太棒了。 您为模板命名的方式真是非同凡响。

非常感谢。我会以此为基础… 非常感谢您分享这份宝贵的内容。

更多关于Golang中如何实现UI组件的复用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢你们的支持。 你们两位都给予了我极大的帮助。

对于像我这样的人来说,从一个框架切换到另一个框架是很困难的。 你们的帮助给了我很多信心,让我能走在正确的道路上。

非常感谢。

Mahesh_Kumar1:

执行这些模板的 Go 文件现在既处理 UI,也处理 UI 所需的数据。这正常吗?

无论它是组件还是基础模板,向模板 {{.}} 传递数据都是正常的。你可以通过简单地使用句点将数据从基础模板传递给组件。

{{template "component" . }}

但我不确定这是否是正确的做法。

如果浏览器中的 HTML 显示正常,那么你就已经用“正确的方式”完成了。

我猜 html/template 这个包正是你所需要的。它允许你将 HTML 代码转换为带有占位符的模板文件。然后,你的 Go 代码就可以用数据来填充这些占位符。

整个过程在这里详细解释会过于冗长,但以下是一些帮助你入门的链接:

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和其他属性。

回到顶部