Golang模板中如何分享自定义函数(FuncMap)

Golang模板中如何分享自定义函数(FuncMap)

引言

我是 Go 语言的新手。

我非常喜欢 Go 语言的模板功能。特别是 Template.Funcs 使得模板功能非常强大。

遗憾的是,官方提供的函数 似乎太少了。

也许是由于性能考虑,或者不想包含太多捆绑功能?我希望模板能尽可能多地使用 Go 语言的语法。

Hugo 中,它实现了许多函数,我发现这些函数非常有用。但可惜的是,我不够聪明,并且认为这个项目目前不太适合新手学习(当然,你也可以从旧的提交记录开始,但这非常困难)。(别担心,只要你的项目相关,无论难度如何,都欢迎分享🙂)

一旦你成为一名 Gopher,我坚信你或多或少都会创造出一些属于你自己的优秀函数。


我到底想做什么?

我创建这个帖子,是希望人们能够分享函数,以便我们都能从中受益。

我理解优秀的函数应该考虑到许多极端情况。但我认为那并不是最重要的事情,你不需要因为追求不完美而担心受到批评,任何分享都是值得的。你可以:

  • ✍️ 直接在这里写一些代码
  • 🔗 分享链接(博客、问题、文章、GitHub 项目等)
  • 💪 建议或改进他人的代码
  • 🧙 解释代码
  • 🙏 询问你想要的函数以及如何实现它。

提前感谢所有回应的人,我对你们的奉献表示最崇高的敬意🙂


更多关于Golang模板中如何分享自定义函数(FuncMap)的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

Hugo dict

如果你对 Hugo 中 dict 的实现感兴趣,这些链接可能对你有用:

不完善的 dict

我创建了一个简单的 dict 供参考。

func Dict(values ...interface{}) (map[string]interface{}, error) {
	if len(values)%2 != 0 {
		return nil, errors.New("parameters must be even")
	}
	dict := make(map[string]interface{})
	var key, val interface{}
	for {
		key, val, values = values[0], values[1], values[2:]
		switch reflect.ValueOf(key).Kind() {
		case reflect.String:
			dict[key.(string)] = val
		default:
			return nil, errors.New(`type must equal to "string"`)
		}
		if len(values) == 0 {
			break
		}
	}
	return dict, nil
}

用法

文本

{{- range $key, $val := (dict "User" "Carson" "Msg" "Test") -}}
{{$key}}: {{$val}}
{{ end -}}

{{ $myDict := dict "Msg" "Hello World" }}
{{$myDict.Msg}}

Go Playground

更多关于Golang模板中如何分享自定义函数(FuncMap)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go模板中分享自定义函数非常实用。以下是一个完整的示例,展示如何定义、注册和使用自定义函数:

package main

import (
    "os"
    "strings"
    "text/template"
    "time"
)

// 自定义函数集合
var templateFuncs = template.FuncMap{
    // 字符串处理
    "toUpper":   strings.ToUpper,
    "toLower":   strings.ToLower,
    "trimSpace": strings.TrimSpace,
    
    // 日期时间格式化
    "formatDate": func(t time.Time, layout string) string {
        return t.Format(layout)
    },
    
    // 数学运算
    "add": func(a, b int) int {
        return a + b
    },
    "multiply": func(a, b int) int {
        return a * b
    },
    
    // 逻辑判断
    "inSlice": func(item string, list []string) bool {
        for _, v := range list {
            if v == item {
                return true
            }
        }
        return false
    },
    
    // 字符串截取
    "truncate": func(s string, length int) string {
        if len(s) <= length {
            return s
        }
        return s[:length] + "..."
    },
    
    // HTML安全输出
    "safeHTML": func(s string) template.HTML {
        return template.HTML(s)
    },
}

func main() {
    // 创建模板并注册函数
    tmpl := template.Must(template.New("demo").Funcs(templateFuncs).Parse(`
{{/* 使用字符串函数 */}}
原始字符串: "{{.Text}}"
大写: {{.Text | toUpper}}
小写: {{.Text | toLower}}
修剪后: "{{.Text | trimSpace}}"
截断(10): {{.Text | truncate 10}}

{{/* 使用日期函数 */}}
当前时间: {{formatDate .Now "2006-01-02 15:04:05"}}

{{/* 使用数学函数 */}}
{{$a := 5}}{{$b := 3}}
{{$a}} + {{$b}} = {{add $a $b}}
{{$a}} × {{$b}} = {{multiply $a $b}}

{{/* 使用逻辑函数 */}}
{{$list := .Items}}
{{range $item := $list}}
- {{$item}} 在列表中吗? {{inSlice $item $list}}
{{end}}

{{/* 使用安全HTML输出 */}}
{{safeHTML "<strong>加粗文本</strong>"}}
`))

    // 模板数据
    data := struct {
        Text  string
        Now   time.Time
        Items []string
    }{
        Text:  "  Hello, Go Templates!  ",
        Now:   time.Now(),
        Items: []string{"apple", "banana", "orange"},
    }

    // 执行模板
    if err := tmpl.Execute(os.Stdout, data); err != nil {
        panic(err)
    }
}

输出示例:

原始字符串: "  Hello, Go Templates!  "
大写:   HELLO, GO TEMPLATES!  
小写:   hello, go templates!  
修剪后: "Hello, Go Templates!"
截断(10):   Hello, ...

当前时间: 2023-10-05 14:30:25

5 + 3 = 8
5 × 3 = 15

- apple 在列表中吗? true
- banana 在列表中吗? true
- orange 在列表中吗? true

<strong>加粗文本</strong>

更多实用函数示例:

// 文件大小格式化
func formatFileSize(bytes int64) string {
    const unit = 1024
    if bytes < unit {
        return fmt.Sprintf("%d B", bytes)
    }
    div, exp := int64(unit), 0
    for n := bytes / unit; n >= unit; n /= unit {
        div *= unit
        exp++
    }
    return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

// 相对时间显示
func relativeTime(t time.Time) string {
    now := time.Now()
    diff := now.Sub(t)
    
    switch {
    case diff < time.Minute:
        return "刚刚"
    case diff < time.Hour:
        return fmt.Sprintf("%d分钟前", int(diff.Minutes()))
    case diff < 24*time.Hour:
        return fmt.Sprintf("%d小时前", int(diff.Hours()))
    default:
        return fmt.Sprintf("%d天前", int(diff.Hours()/24))
    }
}

// 注册到FuncMap
var extendedFuncs = template.FuncMap{
    "fileSize":    formatFileSize,
    "relativeTime": relativeTime,
    // 合并之前的函数
    "toUpper": templateFuncs["toUpper"],
    "toLower": templateFuncs["toLower"],
}

这些函数可以直接在模板中使用:

tmpl := `文件大小: {{.Size | fileSize}}
更新时间: {{.UpdateTime | relativeTime}}`

要分享函数库,可以创建独立的Go模块:

// 在单独包中定义
package templatefuncs

import (
    "html/template"
    "strings"
    "time"
)

// GetFuncMap 返回所有自定义函数
func GetFuncMap() template.FuncMap {
    return template.FuncMap{
        "toUpper":      strings.ToUpper,
        "formatDate":   formatDate,
        "truncate":     truncateString,
        "safeHTML":     safeHTML,
        "fileSize":     formatFileSize,
        "relativeTime": relativeTime,
    }
}

// 在其他项目中导入使用
import "github.com/yourname/templatefuncs"

func main() {
    tmpl := template.New("").Funcs(templatefuncs.GetFuncMap())
}

这种方式允许通过Go模块系统分享和复用自定义模板函数。

回到顶部