模板和自定义函数;恐慌:函数未定义

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

Template and custom function; panic: function not defined

问题

使用html/template,我正在尝试在模板中使用自己的函数。不幸的是,我无法使用go模板的函数映射功能。我得到的错误如下:

% go run test.go
panic: template: tmpl.html:5: function "humanSize" not defined
[...]

简化的测试案例如下(test.go):

package main

import (
    "html/template"
    "io/ioutil"
    "net/http"
    "strconv"
)

var funcMap = template.FuncMap{
    "humanSize": humanSize,
}
var tmplGet = template.Must(template.ParseFiles("tmpl.html")).Funcs(funcMap)

func humanSize(s int64) string {
    return strconv.FormatInt(s/int64(1000), 10) + " KB"
}

func getPageHandler(w http.ResponseWriter, r *http.Request) {
    files, _ := ioutil.ReadDir(".")
    if err := tmplGet.Execute(w, files); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func main() {
    http.HandleFunc("/", getPageHandler)
    http.ListenAndServe(":8080", nil)
}

我有以下简单的模板(tmpl.html):

<html><body>
    {{range .}}
    <div>
        <span>{{.Name}}</span>
        <span>{{humanSize .Size}}</span>
    </div>
    {{end}}
</body></html>

这是go 1.1.1版本。

英文:

Using html/template I am trying to use one of my own functions inside a template. Unfortunately I am unable to use the function map feature of go's templates. All I get is the following error:

% go run test.go
panic: template: tmpl.html:5: function &quot;humanSize&quot; not defined
[...]

The reduced testcase looks as follows (test.go):

package main

import (
    &quot;html/template&quot;
    &quot;io/ioutil&quot;
    &quot;net/http&quot;
    &quot;strconv&quot;
)

var funcMap = template.FuncMap{
    &quot;humanSize&quot;: humanSize,
}
var tmplGet = template.Must(template.ParseFiles(&quot;tmpl.html&quot;)).Funcs(funcMap)

func humanSize(s int64) string {
    return strconv.FormatInt(s/int64(1000), 10) + &quot; KB&quot;
}

func getPageHandler(w http.ResponseWriter, r *http.Request) {
    files, _ := ioutil.ReadDir(&quot;.&quot;)
    if err := tmplGet.Execute(w, files); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func main() {
    http.HandleFunc(&quot;/&quot;, getPageHandler)
    http.ListenAndServe(&quot;:8080&quot;, nil)
}

And I have the following simple template (tmpl.html):

&lt;html&gt;&lt;body&gt;
    {{range .}}
    &lt;div&gt;
        &lt;span&gt;{{.Name}}&lt;/span&gt;
        &lt;span&gt;{{humanSize .Size}}&lt;/span&gt;
    &lt;/div&gt;
    {{end}}
&lt;/body&gt;&lt;/html&gt;

This is go 1.1.1.

答案1

得分: 38

IIRC,模板函数映射必须在解析模板之前通过.Funcs进行定义。下面的代码似乎可以工作。

package main

import (
    "html/template"
    "io/ioutil"
    "net/http"
    "strconv"
)

var funcMap = template.FuncMap{
    "humanSize": humanSize,
}

const tmpl = `
<html><body>
    {{range .}}
    <div>
        <span>{{.Name}}</span>
        <span>{{humanSize .Size}}</span>
    </div>
    {{end}}
</body></html>`

var tmplGet = template.Must(template.New("").Funcs(funcMap).Parse(tmpl))

func humanSize(s int64) string {
    return strconv.FormatInt(s/int64(1000), 10) + " KB"
}

func getPageHandler(w http.ResponseWriter, r *http.Request) {
    files, err := ioutil.ReadDir(".")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    if err := tmplGet.Execute(w, files); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func main() {
    http.HandleFunc("/", getPageHandler)
    http.ListenAndServe(":8080", nil)
}
英文:

IIRC, template functions map must be defined by .Funcs before parsing the template. The below code seems to work.

package main

import (
        &quot;html/template&quot;
        &quot;io/ioutil&quot;
        &quot;net/http&quot;
        &quot;strconv&quot;
)

var funcMap = template.FuncMap{
        &quot;humanSize&quot;: humanSize,
}

const tmpl = `
&lt;html&gt;&lt;body&gt;
    {{range .}}
    &lt;div&gt;
        &lt;span&gt;{{.Name}}&lt;/span&gt;
        &lt;span&gt;{{humanSize .Size}}&lt;/span&gt;
    &lt;/div&gt;
    {{end}}
&lt;/body&gt;&lt;/html&gt;`

var tmplGet = template.Must(template.New(&quot;&quot;).Funcs(funcMap).Parse(tmpl))

func humanSize(s int64) string {
        return strconv.FormatInt(s/int64(1000), 10) + &quot; KB&quot;
}

func getPageHandler(w http.ResponseWriter, r *http.Request) {
        files, err := ioutil.ReadDir(&quot;.&quot;)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }

        if err := tmplGet.Execute(w, files); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        }
}

func main() {
        http.HandleFunc(&quot;/&quot;, getPageHandler)
        http.ListenAndServe(&quot;:8080&quot;, nil)
}

答案2

得分: 1

解决方案是根据@user321277的建议,在函数New()和ParseFiles()中使用相同的名称。

var tmplGet = template.Must(template.New("base.html").Funcs(funcMap).ParseFiles("tmpl/base.html", "tmpl/get.html"))

英文:

The solution is to have same names in the function New() and Parse‌​Files() a suggested by @user321277

var tmplGet = template.Must(template.New(&quot;base.html&quot;).Funcs(funcMap).Parse‌​Files(&quot;tmpl/base.htm‌​l&quot;, &quot;tmpl/get.html&quot;))

huangapple
  • 本文由 发表于 2013年7月25日 03:35:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/17843311.html
匿名

发表评论

匿名网友

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

确定