Golang模板是基于角色的视图的更好方法

时间:2016-03-10 03:28:08

标签: templates go

我正在尝试为基于角色的模板用例提供更好,更有效的方法。

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

代码和模板方法非常重复,必须有更好的方法来获取它。 我可以使用一些“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代码。 我在原始问题代码中添加了更多详细信息。

1 个答案:

答案 0 :(得分:4)

简化代码

您的代码可以大大简化:

  • 您只需要解析模板一次。您可以使用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代码中计算结果,并将结果作为数据传递给执行,或者在模板中注册回调函数并进行模板操作调用该函数并插入返回值。