【问题标题】:Golang templates a better approach for role based viewsGolang 模板是基于角色的视图的更好方法
【发布时间】:2016-03-10 03:28:07
【问题描述】:

我正在尝试为基于角色的模板用例找到更好、更有效的方法。

你有三个不同的角色,内容不同,为了简化示例,每个角色的侧边栏都是相同的。

代码和模板的方式非常重复,一定有更好的方法来获取。 我可以使用一些“if”语句来加载不同的模板,但这很慢。 如果我在模板中使用新的{{block}} 操作,它只会为每个内容保存 1 行。我知道这里有问题,但我无法理解。

提前致谢。

package main

import (
    "html/template"
    "log"
    "os"
)

const t = `
{{define "header"}}==Header=={{end}}
{{define "sidebar"}}==Side Bar=={{end}}
{{define "footer"}}==Footer=={{end}}

{{define "index-role1"}}Welcome Stuff for Role1{{end}}
{{define "index-role2"}}Welcome Stuff for Role2{{end}}
{{define "index-role3"}}Welcome Stuff for Role3{{end}}

{{define "content1-role1"}}Content 1 for Role1{{end}}
{{define "content1-role2"}}Content 1 for Role2{{end}}
{{define "content1-role3"}}Content 1 for Role3{{end}}

{{define "content2-role1"}}Content 2 for Role1{{end}}
{{define "content2-role2"}}Content 2 for Role2{{end}}
{{define "content2-role3"}}Content 2 for Role3{{end}}


{{define "display-role1-index"}}    {{template "header" .}}{{template "sidebar" . }} {{template "index-role1" .}}{{template "footer" .}} {{end}}
{{define "display-role1-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1-role1" .}}{{template "footer" .}}{{end}}
{{define "display-role1-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2-role1" .}}{{template "footer" .}}{{end}}

{{define "display-role2-index"}}    {{template "header" .}}{{template "sidebar" . }} {{template "index-role2" .}}{{template "footer" .}}{{end}}
{{define "display-role2-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1-role2" .}}{{template "footer" .}}{{end}}
{{define "display-role2-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2-role2" .}}{{template "footer" .}}{{end}}

{{define "display-role3-index"}}    {{template "header" .}}{{template "sidebar" . }} {{template "index-role3" .}}{{template "footer" .}}{{end}}
{{define "display-role3-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1-role3" .}}{{template "footer" .}}{{end}}
{{define "display-role3-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2-role3" .}}{{template "footer" .}}{{end}}
`

var templates map[string]*template.Template

type User struct {
    Login string
    Role  string
}

func init() {
    if templates == nil {
        templates = make(map[string]*template.Template)
    }
    //Templates for role1
    templates["role1-index"] = template.Must(template.New("display-role1-index").Parse(t))
    templates["role1-content1"] = template.Must(template.New("display-role1-content1").Parse(t))
    templates["role1-content2"] = template.Must(template.New("display-role1-content2").Parse(t))
    //Templates for role2
    ...
    //Templates for role3
    ...
}

func main() {
    loggedUser := User{Login: "Username1", Role: "role1"}

    // ONLY FOR INDEX
    switch loggedUser.Role {
    case "role1":
        err := templates["role1-index"].Execute(os.Stdout, nil)
        if err != nil {
            log.Println(err.Error())
        }
    case "role2":
        err := templates["role2-index"].Execute(os.Stdout, nil)
        if err != nil {
            log.Println(err.Error())
        }
    case "role3":
        err := templates["role3-index"].Execute(os.Stdout, nil)
        if err != nil {
            log.Println(err.Error())
        }               
    }
    ...
    //CODE FOR CONTENT1
    ...
    //CODE FOR CONTENT2 
    ...
}

编辑: 我在想,如果这样的事情能帮上忙……

const t = `
    {{define "header"}}==Header=={{end}}
    {{define "sidebar"}}==Side Bar=={{end}}
    {{define "footer"}}==Footer=={{end}}

    {{define "display-base"}} 
        {{template "header" .}}
        {{template "sidebar" . }} 
            {{block "content" .}}Welcome {{block "role"}}Role 1{{end}}{{end}}
        {{template "footer" .}} 
    {{end}}`

在我的问题中,我试图简化一些事情来解释我的想法,模板代码中的内容,如“Content 1 Role1”只是表明应该有一些仅用于 Role1 角色视图的 html 代码. 我在原始问题代码中添加了更多详细信息。

【问题讨论】:

    标签: templates go


    【解决方案1】:

    简化代码

    您的代码可以大大简化:

    • 您只需要解析一次模板。您可以使用Template.ExecuteTemplate() 从其名称指定的集合中执行一个。
    • 模板名称很容易从角色名称(存储在user.Role)推导出来,因此您不需要任何开关。

    查看这个简化的解决方案,除了 index 还呈现 content1content2 但比您的要短得多:

    const t = `...` // Your template source here
    
    var templates = template.Must(template.New("all").Parse(t))
    
    type User struct {
        Login string
        Role  string
    }
    
    func main() {
        u := User{Login: "Username1", Role: "role1"}
    
        for _, name := range []string{"index", "content1", "content2"} {
            templName := "display-" + u.Role + "-" + name
            if err := templates.ExecuteTemplate(os.Stdout, templName, nil); err != nil {
                log.Println(err.Error())
            }
        }
    }
    

    它输出:

    ==Header====Side Bar== Welcome Role1==Footer==  ==Header====Side Bar==
        Content 1 Role1==Footer== ==Header====Side Bar== Content 2 Role1==Footer==
    

    如果您将用户角色更改为role2

    ==Header====Side Bar== Welcome Role2==Footer== ==Header====Side Bar==
        Content 1 Role2==Footer== ==Header====Side Bar== Content 2 Role2==Footer==
    

    你可以创建一个助手renderFor()函数:

    func renderFor(u User) {
        for _, name := range []string{"index", "content1", "content2"} {
            templName := "display-" + u.Role + "-" + name
            if err := templates.ExecuteTemplate(os.Stdout, templName, nil); err != nil {
                log.Println(err.Error())
            }
        }
    }
    

    并为多个用户调用它:

    renderFor(User{Login: "Username1", Role: "role1"})
    fmt.Println()
    renderFor(User{Login: "Username2", Role: "role2"})
    

    Go Playground 上试试。

    简化模板

    简化模板的一种方法是不为单独的角色定义单独的模板,而是使用模板操作来呈现不同的内容和/或将不同的数据传递给执行以包含在输出中(以产生不同的内容)。您必须在执行模板时传递角色和其他所需信息,因此可以使用它来区分。例如,您的所有模板都可以替换为:

    const t = `
    {{define "header"}}==Header=={{end}}
    {{define "sidebar"}}==Side Bar=={{end}}
    {{define "footer"}}==Footer=={{end}}
    
    {{define "index"}}Welcome {{.Role}}{{end}}
    
    {{define "content1"}}Content 1 {{.Role}}{{end}}
    
    {{define "content2"}}Content 2 {{.Role}}{{end}}
    
    {{define "display-index"}}    {{template "header" .}}{{template "sidebar" . }} {{template "index" .}}{{template "footer" .}} {{end}}
    {{define "display-content1"}} {{template "header" .}}{{template "sidebar" . }} {{template "content1" .}}{{template "footer" .}}{{end}}
    {{define "display-content2"}} {{template "header" .}}{{template "sidebar" . }} {{template "content2" .}}{{template "footer" .}}{{end}}
    `
    

    请注意,roleX 已从模板名称中消失,这就是重点。

    并执行模板:

    func renderFor(u User) {
        for _, name := range []string{"index", "content1", "content2"} {
            templName := "display-" + name
            if err := templates.ExecuteTemplate(os.Stdout, templName, u); err != nil {
                log.Println(err.Error())
            }
        }
    }
    

    请注意,用户 u 被传递给 ExecuteTemplate()。在Go Playground 上试试吧。

    您的示例可能过于不切实际,这就是我们可以大幅减少它的原因,但这也是更复杂示例的方法。

    另请注意,根据设计理念,模板不应包含复杂逻辑。如果模板中的某些东西(或看起来)太复杂,您应该考虑在 Go 代码中计算结果并将结果作为数据传递给执行,或者在模板中注册一个回调函数并让模板操作调用该函数并插入返回值。

    【讨论】:

    • 好的@icza 谢谢你的回答,但是有没有办法为这样的用例简化模板中的逻辑?
    • 再次感谢@icza,也许我的问题没有被我正确解释,我只是添加了更多关于它的信息。
    猜你喜欢
    • 2010-12-05
    • 1970-01-01
    • 2016-03-15
    • 1970-01-01
    • 2011-04-25
    • 2015-06-09
    • 1970-01-01
    • 2018-11-12
    • 2021-05-06
    相关资源
    最近更新 更多