简体   繁体   English

如何在golang html/template 中的多个地方创建全局变量和更改?

[英]How to create a global variable and change in multiple places in golang html/template?

I am creating a variable in html/template and and changing the value based on a condition.我正在html/template创建一个变量,并根据条件更改值。 But the scope of the value stays only inside the if condition:但是值的范围只停留在if条件内:

{{if .UserData}}
    {{$currentUserId := .UserData.UserId}}
    [<a href="#ask_question">Inside {{$currentUserId}}</a>]
{{else}}
    {{$currentUserId := 0}}
{{end}}
[<a href="#ask_question">outside {{$currentUserId}}</a>]

Inside the if condition I am getting the correct value but outside it is 0 .在 if 条件内,我得到正确的值,但在它之外是0 How can I use the $currentUserId outside the condition?如何在条件之外使用$currentUserId Could someone help me with this?有人可以帮我解决这个问题吗?

Go 1.11 added support for changing values of template variables . Go 1.11 添加了对更改模板变量值的支持。 To define a variable, use := :要定义变量,请使用:=

{{$currentUserId := 0}}

To change its value, use assignment = :要更改其值,请使用 assignment =

{{$currentUserId = .UserData.UserId}}

If the variable is created outside of the {{if}} block but changed inside it, changes will be visible after the {{if}} block.如果变量是在{{if}}块之外创建但在其中更改,则更改将在{{if}}块之后可见。

{{$currentUserId := 0 -}}
Before: {{$currentUserId}}
{{if .UserData -}}
    {{$currentUserId = .UserData.UserId}}
    [<a href="#ask_question">Inside {{$currentUserId}}</a>]
{{else}}
    {{$currentUserId = 0}}
{{end}}
[<a href="#ask_question">outside {{$currentUserId}}</a>]

Testing this like:像这样测试:

m := map[string]interface{}{}
t := template.Must(template.New("").Parse(src))

m["UserData"] = UserData{99}
if err := t.Execute(os.Stdout, m); err != nil {
    panic(err)

The output is (try it on the Go Playground ):输出是(在Go Playground上试试):

Before: 0

    [<a href="#ask_question">Inside 99</a>]

[<a href="#ask_question">outside 99</a>]

Original answer follows.原答案如下。


Short answer is: you can't.简短的回答是:你不能。

By design philosophy, templates should not contain complex logic.根据设计理念,模板不应包含复杂的逻辑。 In templates you can only create new variables, you cannot change the values of existing template variables.在模板中,您只能创建新变量,不能更改现有模板变量的值。 When you do {{$currentUserId := .UserData.UserId}} inside an {{if}} block, that creates a new variable which shadows the outer one, and its scope extends to the {{end}} action.当您在{{if}}块内执行{{$currentUserId := .UserData.UserId}} ,会创建一个新变量,该变量{{$currentUserId := .UserData.UserId}}外部变量,其范围扩展到{{end}}操作。

This is described in the text/template package, Variables section (the same applies to the html/template package too):这在text/template包的变量部分中有描述(同样适用于html/template包):

A variable's scope extends to the "end" action of the control structure ("if", "with", or "range") in which it is declared, or to the end of the template if there is no such control structure.变量的范围扩展到声明它的控制结构(“if”、“with”或“range”)的“结束”操作,如果没有这样的控制结构,则扩展到模板的末尾。 A template invocation does not inherit variables from the point of its invocation.模板调用不会从其调用点继承变量。

Possible workarounds可能的解决方法

Easiest in your case would be to register a custom function CurrentUserId() to your template, which if UserData is present, returns its id UserData.UserId() , and if it is not present, returns 0 as in your case.在您的情况下,最简单的方法是将自定义函数CurrentUserId()注册到您的模板,如果UserData存在,则返回其 ID UserData.UserId() ,如果不存在,则返回0与您的情况一样。

This is an example how it can be done:这是一个如何完成的示例:

type UserData struct {
    UserId int
}

func main() {
    m := map[string]interface{}{}
    t := template.Must(template.New("").Funcs(template.FuncMap{
        "CurrentUserId": func() int {
            if u, ok := m["UserData"]; ok {
                return u.(UserData).UserId
            }
            return 0
        },
    }).Parse(src))

    if err := t.Execute(os.Stdout, m); err != nil {
        panic(err)
    }
    m["UserData"] = UserData{99}
    if err := t.Execute(os.Stdout, m); err != nil {
        panic(err)
    }
}

const src = `Current user id: {{CurrentUserId}}
`

It first executes the template without UserData , then it executes the template with UserData having UserId = 99 .它首先执行没有UserData的模板,然后执行具有UserId = 99 UserData的模板。 The output is:输出是:

Current user id: 0
Current user id: 99

Try it on the Go Playground .Go Playground上试试。

Simulating changeable variables模拟可变变量

You can also simulate changeable variables, but also with registered custom function(s).您还可以模拟可变变量,但也可以使用已注册的自定义函数。 You could register a function like SetCurrentUserId() which would change the value of a variable, preferably in the params that is passed to the template execution so it remains safe for concurrent use.你可以注册一个像SetCurrentUserId()这样的函数,它会改变一个变量的值,最好是在传递给模板执行的参数中,这样它就可以安全地并发使用。

This is an example how to do it (it uses a map as template data, and SetCurrentUserId() sets the current user id as a value in this map):这是一个如何做到的示例(它使用map作为模板数据,并且SetCurrentUserId()将当前用户 ID 设置为该地图中的一个值):

func main() {
    m := map[string]interface{}{}
    t := template.Must(template.New("").Funcs(template.FuncMap{
        "SetCurrentUserId": func(id int) string {
            m["CurrentUserId"] = id
            return ""
        },
    }).Parse(src))

    if err := t.Execute(os.Stdout, m); err != nil {
        panic(err)
    }
    m["UserData"] = UserData{99}
    if err := t.Execute(os.Stdout, m); err != nil {
        panic(err)
    }
}

const src = `Before: {{.CurrentUserId}}
{{if .UserData}}
    {{SetCurrentUserId .UserData.UserId}}Inside: {{.CurrentUserId}}
{{else}}
    {{SetCurrentUserId 0}}Inside: {{.CurrentUserId}}
{{end}}
After: {{.CurrentUserId}}
`

This again first executes the template without UserData , then it executes the template with UserData having UserId = 99 .这再次首先执行没有UserData的模板,然后执行具有UserId = 99 UserData模板。 The output is:输出是:

Before: 
    Inside: 0
After: 0
Before: 0
    Inside: 99
After: 99

Try it on the Go Playground .Go Playground上试试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM