Golang:解析目录和子目录中的所有模板?

时间:2016-07-31 17:21:58

标签: go

这是我的目录结构:

app/
  template/
    layout/
      base.tmpl
    index.tmpl

template.ParseGlob("*/*.tmpl")index.tmpl子目录中解析base.tmpl但不解析layout。有没有办法递归地解析所有模板?

6 个答案:

答案 0 :(得分:11)

我没有实现你自己的功能,我一直在使用这样的东西

func ParseTemplates() *template.Template {
    templ := template.New("")
    err := filepath.Walk("./views", func(path string, info os.FileInfo, err error) error {
        if strings.Contains(path, ".html") {
            _, err = templ.ParseFiles(path)
            if err != nil {
                log.Println(err)
            }
        }

        return err
    })

    if err != nil {
        panic(err)
    }

    return templ
}

这将解析您的所有模板,然后您可以通过调用它们的名称来渲染它们,例如

template.ExecuteTemplate(w, "home", nil)

答案 1 :(得分:3)

Datsik的答案有一个缺点,即当多个目录包含许多模板时会出现名称冲突问题。如果不同目录中的两个模板具有相同的文件名,则它将无法正常工作:只有第二个模板可用。

这是由template.ParseFiles的实现引起的,所以我们可以通过避免template.ParseFiles来解决它。这是一个改进的步行算法,它做到了这一点;它直接使用template.Parse。

func findAndParseTemplates(rootDir string, funcMap template.FuncMap) (*template.Template, error) {
    cleanRoot := filepath.Clean(rootDir)
    pfx := len(cleanRoot)+1
    root := template.New("")

    err := filepath.Walk(cleanRoot, func(path string, info os.FileInfo, e1 error) error {
        if !info.IsDir() && strings.HasSuffix(path, ".html") {
            if e1 != nil {
                return e1
            }

            b, e2 := ioutil.ReadFile(path)
            if e2 != nil {
                return e2
            }

            name := path[pfx:]
            t := root.New(name).Funcs(funcMap)
            t, e2 = t.Parse(string(b))
            if e2 != nil {
                return e2
            }
        }

        return nil
    })

    return root, err
}

这将解析您的所有模板,然后您可以通过调用它们的名称来渲染它们,例如

template.ExecuteTemplate(w, "a/home.html", nil)

答案 2 :(得分:0)

如果它不是深度嵌套的(如果您事先知道子目录的名称),则可以这样做:

t := template.Must(template.ParseGlob("template/*.tmpl"))
template.Must(t.ParseGlob("template/layout/*.tmpl"))

然后每个子目录与“布局”相同

答案 3 :(得分:0)

您可以通过这种方式加载多个子目录。在这里,我们忽略子目录是否不存在。但是我们要确保可以加载第一个目录。

func ParseTemplates() (*template.Template, error) {
    templateBuilder := template.New("")
    if t, _ := templateBuilder.ParseGlob("/*/*/*/*/*.tmpl"); t != nil {
        templateBuilder = t
    }
    if t, _ := templateBuilder.ParseGlob("/*/*/*/*.tmpl"); t != nil {
        templateBuilder = t
    }
    if t, _ := templateBuilder.ParseGlob("/*/*/*.tmpl"); t != nil {
        templateBuilder = t
    }
    if t, _ := templateBuilder.ParseGlob("/*/*.tmpl"); t != nil {
        templateBuilder = t
    }
    return templateBuilder.ParseGlob("/*.tmpl")
}

答案 4 :(得分:0)

我制作了一个包来解决这个问题。

https://github.com/karelbilek/template-parse-recursive

package main

import (
    "html/template"
    "os"

    recurparse "github.com/karelbilek/template-parse-recursive"
)

func main() {
    t, err := recurparse.HTMLParse(
        template.New("templates"), 
        "path/to/templates", 
        "*.html",
    )
   
    if err != nil {
        panic(err)
    }
    
    templateUnder := t.Lookup("subdir/subdir/template.html")
    templateUnder.Execute(os.Stdout, nil)
}

答案 5 :(得分:0)

有了 pete911 的回答,我可以创建这个函数来解析多个子目录中的所有 html 模板。

// parse.go
func ParseHtmlTemplates() *template.Template {
    var directories []string
    var filenames []string

    // Root directory of template files
    root := "./templates"

    // Get all directories on /templates and check if there's repeated files
    err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
        if !info.IsDir() {
            // Is file
            filename := info.Name()

            hasRepeatedFiles := contains(filenames, filename)
            if hasRepeatedFiles {
                return fmt.Errorf("You can't have repeated template files: %s", filename)
            }

            filenames = append(filenames, filename)
        } else {
            // Is directory
            directories = append(directories, path)
        }

        return nil
    })
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    // Create a template for parsing all directories
    tmpl := template.Must(template.ParseGlob(root + "/*.html"))

    // Parse all directories (minus the root directory)
    for _, path := range directories[1:] {
        pattern := path + "/*.html"
        template.Must(tmpl.ParseGlob(pattern))
    }

    return tmpl
}

// contains private method
func contains(filenames []string, filename string) bool {
    for _, f := range filenames {
        if f == filename {
            return true
        }
    }
    return false
}

// main.go
func main() {
    tmpl = ParseHtmlTemplates()
    if err := tmpl.Execute(os.Stdout, ""); err != nil {
        panic(err)
    }
}