Usage of variables in a nested template which is also defined as a variable in a go template?

huangapple go评论73阅读模式
英文:

Usage of variables in a nested template which is also defined as a variable in a go template?

问题

package main

import (
	"os"
	"text/template"
)

type Todo struct {
	Name        string
	Description string
	Subtemplate string
}

func main() {
	td := Todo{
		Name:        "Test name",
		Description: "Test description",
		Subtemplate: "Subtemplate {{.Name}}",
	}

	t, err := template.New("todos").Parse(`{{template "subtemplate" .}} You have a task named "{{ .Name}}" with description: "{{ .Description}}"`)
	if err != nil {
		panic(err)
	}

	// Define the subtemplate
	_, err = t.New("subtemplate").Parse(td.Subtemplate)
	if err != nil {
		panic(err)
	}

	err = t.Execute(os.Stdout, td)
	if err != nil {
		panic(err)
	}
}

上述代码的结果是:

Subtemplate Test name You have a task named "Test name" with description: "Test description"

这意味着子模板中的变量.Name没有被解析(可能是因为设计上不可能,需要一种递归调用的方式)。有没有其他方法可以实现这个效果?

对于使用template.FuncMap定义的模板函数也应该适用。谢谢。

英文:

The idea is to define a variable for a go template which is also a template using variables (a nested template) like this:

package main

import (
	"os"
	"text/template"
)

type Todo struct {
	Name        string
	Description string
	Subtemplate string
}

func main() {
	td := Todo{
		Name: "Test name",
		Description: "Test description",
		Subtemplate: "Subtemplate {{.Name}}",
	}

	t, err := template.New("todos").Parse("{{.Subtemplate}} You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
	if err != nil {
		panic(err)
	}
	err = t.Execute(os.Stdout, td)
	if err != nil {
		panic(err)
	}
}

The result of the code above is however:

Subtemplate {{.Name}} You have a task named "Test name" with description: "Test description"

means the variable .Name in the subtemplate is not resolved (probably by design not possible, would require some kind of a recursive call). Is there any/other way to achieve this effect?

It should work for the template functions defined using template.FuncMap too. Thanx.

答案1

得分: 2

你可以注册一个函数来解析字符串模板并执行它。

该函数可以像这样:

func exec(body string, data interface{}) (string, error) {
    t, err := template.New("").Parse(body)
    if err != nil {
        return "", err
    }
    buf := &strings.Builder{}
    err = t.Execute(buf, data)
    return buf.String(), err
}

你将模板主体文本和用于模板执行的数据传递给它。它会执行并返回结果。

一旦注册,你可以在模板中这样调用它:

{{exec .Subtemplate .}}

完整示例:

td := Todo{
    Name:        "Test name",
    Description: "Test description",
    Subtemplate: "Subtemplate {{.Name}}",
}

t, err := template.New("todos").Funcs(template.FuncMap{
    "exec": func(body string, data interface{}) (string, error) {
        t, err := template.New("").Parse(body)
        if err != nil {
            return "", err
        }
        buf := &strings.Builder{}
        err = t.Execute(buf, data)
        return buf.String(), err
    },
}).Parse("{{exec .Subtemplate .}} You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
if err != nil {
    panic(err)
}
err = t.Execute(os.Stdout, td)
if err != nil {
    panic(err)
}

这将输出(在Go Playground上尝试):

Subtemplate Test name You have a task named "Test name" with description: "Test description"

请注意,如果子模板在运行时不会更改,你应该预先解析它并将生成的模板(*template.Template)存储起来,以避免每次执行模板时都进行解析。

英文:

You can register a function which parses a string template and executes it.

The function could look like this:

func exec(body string, data any) (string, error) {
    t, err := template.New("").Parse(body)
    if err != nil {
        return "", err
    }
    buf := &strings.Builder{}
    err = t.Execute(buf, data)
    return buf.String(), err
}

You pass the template body text and the data for template execution to it. It executes it and returns the result.

Once registered, you can call it like this from a template:

{{exec .Subtemplate .}}

Full example:

td := Todo{
	Name:        "Test name",
	Description: "Test description",
	Subtemplate: "Subtemplate {{.Name}}",
}

t, err := template.New("todos").Funcs(template.FuncMap{
	"exec": func(body string, data any) (string, error) {
		t, err := template.New("").Parse(body)
		if err != nil {
			return "", err
		}
		buf := &strings.Builder{}
		err = t.Execute(buf, data)
		return buf.String(), err
	},
}).Parse("{{exec .Subtemplate .}} You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
if err != nil {
	panic(err)
}
err = t.Execute(os.Stdout, td)
if err != nil {
	panic(err)
}

This will output (try it on the Go Playground):

Subtemplate Test name You have a task named "Test name" with description: "Test description"

Note that if the subtemplate does not change during runtime, you should pre-parse it and store the resulting template (*template.Template) to avoid having to parse it each time you execute the template.

答案2

得分: 1

如果你不介意性能问题,你可以渲染两次...

package main

import (
    "os"
    "text/template"
    "bytes"
)

type Todo struct {
    Name        string
    Description string
    Subtemplate string
}

func main() {
    td := Todo{
        Name:        "测试名称",
        Description: "测试描述",
        Subtemplate: "子模板 {{.Name}}",
    }

    t, err := template.New("todos").Parse("{{.Subtemplate}} 你有一个名为“{{.Name}}”的任务,描述为:“{{.Description}}”")
    if err != nil {
        panic(err)
    }
    buffer := &bytes.Buffer{}
    err = t.Execute(buffer, td)
    tc, err := template.New("todo2").Parse(string(buffer.Bytes()))
    if err != nil {
        panic(err)
    }
    err = tc.Execute(os.Stdout, td)
    if err != nil {
        panic(err)
    }
}
英文:

You can render twice if you don't mind for peformance ...

package main

import (
    "os"
    "text/template"
    "bytes"
)

type Todo struct {
    Name        string
    Description string
    Subtemplate string
}

func main() {
    td := Todo{
        Name: "Test name",
        Description: "Test description",
        Subtemplate: "Subtemplate {{.Name}}",
    }

    t, err := template.New("todos").Parse("{{.Subtemplate}} You have a task named \"{{ .Name}}\" with description: \"{{ .Description}}\"")
    if err != nil {
        panic(err)
    }
    buffer := &bytes.Buffer{}
    err = t.Execute(buffer, td)
    tc, err := template.New ("todo2").Parse(string (buffer.Bytes ()))
    if err != nil {
        panic (err)
    }
    err = tc.Execute(os.Stdout, td)
    if err != nil {
        panic(err)
    }
}

huangapple
  • 本文由 发表于 2023年1月10日 15:48:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75066909.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定