如何将数据添加到在Go中作为参数的接口?

时间:2017-03-16 14:04:20

标签: go go-templates

我只是接受,如果我的术语不准确,请道歉。我的最终目标是将缓存的CSS文件的名称添加到我的Go应用程序的布局模板中。应用程序启动时动态构建的CSS文件无法进行硬编码。在我的模板文件中,我有这个:

//More html here
<link href="{{.CSSFile}}" rel="stylesheet">
//more html here 

我在View类型上有一个Render方法,如下所示。它需要data interface{}作为参数,然后运行ExecuteTemplate。它由每个控制器以这种或那种方式调用,它发送data参数并公开信息。我知道如何将它作为来自控制器的数据添加,然后调用Render方法,但我显然不希望在每个控制器动作中添加CSS文件,因此最有意义的是将其添加到Render函数一次,并将其添加到传递给ExecuteTemplate的数据中。我的问题是如何将这些信息添加到已传递给Render的数据中,然后将整个信息传递给ExecuteTemplate。我下面的内容适用于CSS文件,但它显然不会传递给传递给原始data参数的Render

type View struct {
    Template *template.Template
    Layout   string
}


func (v *View) Render(w http.ResponseWriter, data interface{}) error {
    d := Data{}
    d.AddCSSFile()
    w.Header().Set("Content-Type", "text/html")
    err := v.Template.ExecuteTemplate(w, v.Layout, d)
    if err != nil {
        log.Println(err)
        fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
    }
    return nil
}

type Data struct {
    Alerts  []Alert
    Yield   interface{}
    CSSFile interface{}
}


func (d *Data) AddCSSFile() {
    ss, _ := filepath.Glob("./assets/site-*.css")
    fp := strings.Join(ss, "")
    _, d.CSSFile = filepath.Split(fp)
}

我已经创造了一个要点,但并非完全完整,从我正在尝试做的事情中更加充实: https://gist.github.com/codelitt/549a68149add0482c6dc2514a46aa580

1 个答案:

答案 0 :(得分:2)

我不确定我确切地理解您要问的是什么,但如果你想要的是将data interface{}参数与d := Data{}内的Render值相结合,那么你可以做点什么像这样...

// ...

func (v *View) Render(w http.ResponseWriter, data interface{}) error {
    p := Page{Data:data}
    p.AddCSSFile()
    w.Header().Set("Content-Type", "text/html")
    err := v.Template.ExecuteTemplate(w, v.Layout, p)
    if err != nil {
        log.Println(err)
        fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
    }
    return nil
}

type Page struct {
    Alerts  []Alert
    Yield   interface{}
    CSSFile interface{}
    Data interface{}
}
func (p *Page) AddCSSFile() {
     // ...
}

修改或者您也可以初始化匿名结构值并将其传递给ExecuteTemplate,而无需更改现有的Data类型。

// ...

err := v.Template.ExecuteTemplate(w, v.Layout, struct{
    Data
    Args interface{}
}{Data:d, Args:data})

// ...

Edit2:因此,如果我正确理解您的评论,传递给data interface{}方法的Render参数可能在某些或所有实例中都是或包含一个类型的值匹配其中一个Data字段&#39;类型,在这种情况下,您希望将该值设置为该字段,以便将它们一起传递给ExecuteTemplate方法。正如您已经发现的那样,至少有一个解决方案是使用类型断言。以下是来自问题中原始示例的上下文中的注释的示例的略微修改版本。

func (v *View) Render(w http.ResponseWriter, data interface{}) error {
    d := Data{}
    d.AddCSSFile()
    if alerts, ok := data.([]Alert); ok {
        d.Alerts = alerts
    }

    w.Header().Set("Content-Type", "text/html")
    err := v.Template.ExecuteTemplate(w, v.Layout, d)
    if err != nil {
        log.Println(err)
        fmt.Fprintln(w, "<h1>Something went wrong. Please contact us at support")
    }
    return nil
}