Go模板无法在字段上调用方法

时间:2019-10-16 19:14:02

标签: templates go go-templates

我在net/mail.Address周围有一个包装器,提供了一些编组逻辑。我正在尝试在模板中使用它,但是我不断得到can't evaluate field String in type EmailAddress。模板文档说:

  

数据的尼拉德方法的名称,后跟一个句点,   例如

     

。方法

     

结果是调用以dot作为方法的值   接收者dot.Method()。

  

方法调用可以链接在一起,并与字段和键组合   到任何深度:

     

.Field1.Key1.Method1.Field2.Key2.Method2

因此我谨记这一点:

package main

import (
    "bytes"
    "fmt"
    "html/template"
    "net/mail"
    "os"
)

type EmailAddress struct{ mail.Address }

type emailFormatter struct {
    From         EmailAddress
    To           EmailAddress
}

var tmpl = template.Must(template.New("Sample Text").Parse("From: {{.From.String}}\r" + `
To: {{.To.String}}` + "\r" + `
Content-Type: text/html` + "\r" + `
Subject: Sample Text` + "\r\n\r" + `
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Sample Text</title>
    <meta charset="utf-8"/>
</head>
<body>
    Sample Text
</body>
</html>
`));

func main() {
    to := EmailAddress{
        mail.Address{
            Address: "em@i.l",
            Name: "",
        },
    }
    from := EmailAddress{
        mail.Address{
            Address: "no-reply@test.quest",
            Name: "",
        },
    }

    fmt.Println(to.String()) //outputs (as expected) "<em@i.l>"
    fmt.Println(from.String()) //outputs (as expected) "<no-reply@test.quest>"

    f := emailFormatter{
        To: to,
        From: from,
    }

    var buff bytes.Buffer
    if err := tmpl.Execute(&buff, f); err != nil {
        fmt.Printf("Error: %v\n", err)
        os.Exit(1)
    }
    fmt.Println(buff.String())
}

我已经单独验证了调用EmailAddress.String是完全合法的,所以我不知道为什么唯一的输出是:

Error: template: Sample Text:1:13: executing "Sample Text" at <.From.String>: can't evaluate field String in type main.EmailAddress

编辑

在评论者的建议下,我将通话从.From.String.To.String更改为.From.Address.String.To.Address.String,因为

  

String未在EmailAddress上定义,而是在EmailAddress.Address

上定义的

但结果相同:

Error: template: Sample Text:1:13: executing "Sample Text" at <.From.Address.String>: can't evaluate field String in type mail.Address

1 个答案:

答案 0 :(得分:2)

由于String是使用指针接收器定义的,因此您需要将mail.Address的“可寻址”实例传递到模板以能够执行该方法。

您可以通过传递指向f的指针来做到这一点。

if err := tmpl.Execute(&buff, &f); err != nil {
    panic(err)
}

或者您也可以通过传递指向EmailAddress的指针来实现。

type emailFormatter struct {
    From *EmailAddress
    To   *EmailAddress
}

// ...

f := emailFormatter{
    To:   &to,
    From: &from,
}

// ...

if err := tmpl.Execute(&buff, f); err != nil {
    panic(err)
}

或者通过传递指向mail.Address的指针。

type EmailAddress struct{ *mail.Address }

// ...

to := EmailAddress{
    &mail.Address{
        Address: "em@i.l",
        Name: "",
    },
}
from := EmailAddress{
    &mail.Address{
        Address: "no-reply@test.quest",
        Name: "",
    },
}

f := emailFormatter{
    To:   to,
    From: from,
}

// ...

if err := tmpl.Execute(&buff, f); err != nil {
    panic(err)
}

请注意,您不需要在Go代码中执行此操作的原因是因为编译器在那里为您完成了操作。

例如:

fmt.Println(to.String())

成为:

fmt.Println((&to).String())
  

如果x的方法集(的类型)有效,则x.m()方法调用有效   包含m并且参数列表可以分配给参数列表   的米如果x是可寻址的并且&x的方法集包含m,则x.m()为   (&x).m()

的简写