在Go语言中将JSON数据映射到HTML模板

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

Map Json Data To Html Template In GoLang

问题

我正在循环遍历我的 API 响应,并将其添加到 HTML 模板中,代码如下:

// 将相同的信息作为 HTML 发送到浏览器
t, err := template.New("TopMovies").Parse(`
  {{define "TopMovies"}}
    <html>
    <ul>
    {{$ImgUrl := "http://image.tmdb.org/t/p/w185" }}
    {{range $movies := .Results}}
    <li>{{$ImgUrl}}{{$movies.PosterPath}}</li>
    <li>{{$movies.Adult}}</li>
    <li>{{$movies.Overview}}</li>
    <li>{{$movies.ReleaseDate}}</li>
    <li>{{$movies.GenreIds}}</li>
    <li>{{$movies.Id}}</li>
    <li>{{$movies.OriginalTitle}}</li>
    <li>{{$movies.OriginalLanguage}}</li>
    <li>{{$movies.Title}}</li>
    <li>{{$ImgUrl}}{{$movies.BackdropPath}}</li>
    <li>{{$movies.Popularity}}</li>
    <li>{{$movies.VoteCount}}</li>
    <li>{{$movies.Video}}</li>
    <li>{{$movies.VoteAverage}}</li>
    {{end}}
    </ul>
    </html>
  {{end}}
`)
err = t.ExecuteTemplate(w, "T", p) // 这将写入客户端响应

我认为我应该能够在我的 HTML 模板中像这样调用它:

{{.TopMovies}}

但是当我运行应用程序时,数据不会显示在我调用它的 HTML 页面中。我在这里漏掉了什么?

我创建了以下结构体:

// 页面结构体
type Page struct {
  Title     string
  TopMovies string
}

然后我这样创建处理函数:

func TopMoviesHandler(w http.ResponseWriter, r *http.Request) {
   res, err := http.Get(url)
      if err != nil {
        panic(err)
      }
      defer res.Body.Close()

      body, err := ioutil.ReadAll(res.Body)
      if err != nil {
        panic(err)
      }
      var p Payload

      err = json.Unmarshal(body, &p)
      if err != nil {
        panic(err)
      }

  // 将相同的信息作为 HTML 发送到浏览器
     t, err := template.New("TopMovies").Parse(`
      {{define "TopMovies"}}
        <html>
        <ul>
        {{$ImgUrl := "http://image.tmdb.org/t/p/w185" }}
        {{range $movies := .Results}}
        <li>{{$ImgUrl}}{{$movies.PosterPath}}</li>
        <li>{{$movies.Adult}}</li>
        <li>{{$movies.Overview}}</li>
        <li>{{$movies.ReleaseDate}}</li>
        <li>{{$movies.GenreIds}}</li>
        <li>{{$movies.Id}}</li>
        <li>{{$movies.OriginalTitle}}</li>
        <li>{{$movies.OriginalLanguage}}</li>
        <li>{{$movies.Title}}</li>
        <li>{{$ImgUrl}}{{$movies.BackdropPath}}</li>
        <li>{{$movies.Popularity}}</li>
        <li>{{$movies.VoteCount}}</li>
        <li>{{$movies.Video}}</li>
        <li>{{$movies.VoteAverage}}</li>
        {{end}}
        </ul>
        </html>
      {{end}}
      `)
    err = t.ExecuteTemplate(w, "T", p) // 这将写入客户端响应
}

然后在 main.go 中:

http.HandleFunc("/TopPicks", TopMoviesHandler)

TopPicks.html 中:

{{define "TopPicks"}}
    {{template "header" .}}
    <div class="content">
    {{.TopMovies}}
    </div>
     {{template "footer" .}}
{{end}}

以下代码是有效的:

func aboutHandler(w http.ResponseWriter, r *http.Request) {
  display(w, "about", &Page{Title: "About"})
}

我可以像之前提到的那样使用 display() 来为页面添加标题。

在 HTML 模板中:

<title>{{.Title}}</title>

如何使这对于我的 JSON 响应起作用?

英文:

I am looping through my API response and adding it to the html template like this,

 // Following sends same information as above to the browser as html
t, err := template.New(&quot;TopMovies&quot;).Parse(`
{{define &quot;TopMovies&quot;}}
&lt;html&gt;
&lt;ul&gt;
{{$ImgUrl := &quot;http://image.tmdb.org/t/p/w185&quot; }}
{{range $movies := .Results}}
&lt;li&gt;{{$ImgUrl}}{{$movies.PosterPath}}&lt;/li&gt;
&lt;li&gt;{{$movies.Adult}}&lt;/li&gt;
&lt;li&gt;{{$movies.Overview}}&lt;/li&gt;
&lt;li&gt;{{$movies.ReleaseDate}}&lt;/li&gt;
&lt;li&gt;{{$movies.GenreIds}}&lt;/li&gt;
&lt;li&gt;{{$movies.Id}}&lt;/li&gt;
&lt;li&gt;{{$movies.OriginalTitle}}&lt;/li&gt;
&lt;li&gt;{{$movies.OriginalLanguage}}&lt;/li&gt;
&lt;li&gt;{{$movies.Title}}&lt;/li&gt;
&lt;li&gt;{{$ImgUrl}}{{$movies.BackdropPath}}&lt;/li&gt;
&lt;li&gt;{{$movies.Popularity}}&lt;/li&gt;
&lt;li&gt;{{$movies.VoteCount}}&lt;/li&gt;
&lt;li&gt;{{$movies.Video}}&lt;/li&gt;
&lt;li&gt;{{$movies.VoteAverage}}&lt;/li&gt;
{{end}}
&lt;/ul&gt;
&lt;/html&gt;
{{end}}
`)
err = t.ExecuteTemplate(w, &quot;T&quot;, p) // This writes the client response
}

I am under the impression I should be able to call this in my html templates like this,

{{.TopMovies}}

But when I run the app the data does not appear in the html page I am calling it in. What am I missing here?

I create a struct like this,

//A Page structure
type Page struct {
Title string
TopMovies string
}

Then I create my handle like this,

func TopMoviesHandler(w http.ResponseWriter, r *http.Request) {
res, err := http.Get(url)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
var p Payload
err = json.Unmarshal(body, &amp;p)
if err != nil {
panic(err)
}
// Following sends same information as above to the browser as html
t, err := template.New(&quot;TopMovies&quot;).Parse(`
{{define &quot;TopMovies&quot;}}
&lt;html&gt;
&lt;ul&gt;
{{$ImgUrl := &quot;http://image.tmdb.org/t/p/w185&quot; }}
{{range $movies := .Results}}
&lt;li&gt;{{$ImgUrl}}{{$movies.PosterPath}}&lt;/li&gt;
&lt;li&gt;{{$movies.Adult}}&lt;/li&gt;
&lt;li&gt;{{$movies.Overview}}&lt;/li&gt;
&lt;li&gt;{{$movies.ReleaseDate}}&lt;/li&gt;
&lt;li&gt;{{$movies.GenreIds}}&lt;/li&gt;
&lt;li&gt;{{$movies.Id}}&lt;/li&gt;
&lt;li&gt;{{$movies.OriginalTitle}}&lt;/li&gt;
&lt;li&gt;{{$movies.OriginalLanguage}}&lt;/li&gt;
&lt;li&gt;{{$movies.Title}}&lt;/li&gt;
&lt;li&gt;{{$ImgUrl}}{{$movies.BackdropPath}}&lt;/li&gt;
&lt;li&gt;{{$movies.Popularity}}&lt;/li&gt;
&lt;li&gt;{{$movies.VoteCount}}&lt;/li&gt;
&lt;li&gt;{{$movies.Video}}&lt;/li&gt;
&lt;li&gt;{{$movies.VoteAverage}}&lt;/li&gt;
{{end}}
&lt;/ul&gt;
&lt;/html&gt;
{{end}}
`)
err = t.ExecuteTemplate(w, &quot;T&quot;, p) // This writes the client response
}

Then in main.go

   http.HandleFunc(&quot;/TopPicks&quot;, TopMoviesHandler)
TopPicks.html
{{define &quot;TopPicks&quot;}}
{{template &quot;header&quot; .}}
&lt;div class=&quot;content&quot;&gt;
{{.TopMovies}}
&lt;/div&gt;
{{template &quot;footer&quot; .}}
{{end}}

What does work is this,

func aboutHandler(w http.ResponseWriter, r *http.Request) {
display(w, &quot;about&quot;, &amp;Page{Title: &quot;About&quot;})
}

I can add a title to the page in the same way as I previously mentioned but using display()

And in the html template

&lt;title&gt;{{.Title}}&lt;/title&gt;

How can I make this work for my json response?

答案1

得分: 3

看起来你正在使用{{define "body"}},但是却要求ExecuteTemplate执行一个名为"T"的模板,而这个模板在任何地方都没有定义。

我认为你想要的是:t.ExecuteTemplate(w, "body", p)

话虽如此,如果你只想使用多个模板,你可以通过创建一个主模板,然后将所有部分解析为子模板来实现。

这里有一个示例(在Play中查看)。

可以轻松地更改以遍历你的文件系统并加载所有模板,然后只需执行与http.Request路径匹配的模板。

package main

import "html/template"
import "os"
import "log"

var mainText = `
Normal page stuff
{{ template "_header_" . }}
{{ template "body" . }}
`

var bodyText = `
 Body has: {{ .Thing }}
`
var headerText = `
 I am header text
`

type Stuff struct {
    Thing string
}

func main() {
    t := template.New("everything")

    // 解析所有可能需要的模板
    template.Must(t.New("/").Parse(mainText))
    template.Must(t.New("_header_").Parse(headerText))
    template.Must(t.New("body").Parse(bodyText))

    if err := t.ExecuteTemplate(os.Stdout, "/", Stuff{"I am a thing"}); err != nil {
        log.Fatal("Failed to execute:", err)
    }
}
英文:

Looks like you are doing {{define &quot;body&quot;}}, but then asking ExecuteTemplate to execute "T" which isn't defined anywhere.

I think you want: t.ExecuteTemplate(w, &quot;body&quot;, p)

That all said, if you just want to use multiple templates, you can do it by creating a master top level template, then parsing all the parts as sub templates.

Here's an example (on Play).

Easily changed to walk your file system and load all your templates, then you just execute the template matching the http.Request path.

package main
import &quot;html/template&quot;
import &quot;os&quot;
import &quot;log&quot;
var mainText = `
Normal page stuff
{{ template &quot;_header_&quot; . }}
{{ template &quot;body&quot; . }}
`
var bodyText = `
Body has: {{ .Thing }}
`
var headerText = `
I am header text
`
type Stuff struct {
Thing string
}
func main() {
t := template.New(&quot;everything&quot;)
// parse all templates you may want
template.Must(t.New(&quot;/&quot;).Parse(mainText))
template.Must(t.New(&quot;_header_&quot;).Parse(headerText))
template.Must(t.New(&quot;body&quot;).Parse(bodyText))
if err := t.ExecuteTemplate(os.Stdout, &quot;/&quot;, Stuff{&quot;I am a thing&quot;}); err != nil {
log.Fatal(&quot;Failed to execute:&quot;, err)
}
}

答案2

得分: 1

好的,以下是翻译好的内容:

好的,我认为原始代码有两个问题。

  1. 处理程序调用了错误的模板
func TopMoviesHandler(w http.ResponseWriter, r *http.Request) {
   ...省略原始代码...

    err = t.ExecuteTemplate(w, "T", p) // 这会写入客户端响应
    
    应该是

    err = t.ExecuteTemplate(w, "TopMovies", p) // 这会写入客户端响应
  1. 错误的上下文被传递给嵌套模板

在 HTML 文件中有这段代码

{{define "TopPicks"}}
     {{template "header" .}}
     <div class="content">
          {{.TopMovies}}
          {{template "MyTemplate" . }}
     </div>
     {{template "footer" .}}
{{end}}

应该是

{{define "TopPicks"}}
     {{template "header" .}}
     <div class="content">
          {{.TopMovies}}
          {{template "MyTemplate" .TopMovies }}
     </div>
     {{template "footer" .}}
{{end}}

原因是你试图将主上下文传递给嵌套模板,而不是只传递模板期望的 JSON 结果。

原始答案

我制作了一个简单的示例,展示了我认为你需要做的事情。在设置 topMovies 变量的位置,这是你将从 API 调用中获取结果的地方。希望这能更好地展示你需要遵循的顺序。

package main

import (
	"net/http"
	"text/template"
)

type movie struct {
	Title string
}

type page struct {
	Title     string
	TopMovies []movie
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		w.Header().Add("Content Type", "text/html")

		templates := template.New("template")
		templates.New("Body").Parse(doc)
		templates.New("List").Parse(docList)

		topMovies := []movie{{Title: "Movie 1"}, {Title: "Movie 2"}, {Title: "Movie 3"}}

		page := page{Title: "My Title", TopMovies: topMovies}
		templates.Lookup("Body").Execute(w, page)

	})

	http.ListenAndServe(":8000", nil)
}

const docList = `
<ul >
	{{range .}}
	<li>{{.Title}}</li>
	{{end}}
</ul>
`

const doc = `
<!DOCTYPE html>
<html>
	<head><title>{{.Title}}</title></head>
	<body>
		<h1>Hello Templates</h1>
		{{template "List" .TopMovies}}
	</body>
</html>
`
英文:

Ok, I think there were two problems with the original code.

  1. Handler was calling wrong template

func TopMoviesHandler(w http.ResponseWriter, r *http.Request) {
...snip original code...

err = t.ExecuteTemplate(w, &quot;T&quot;, p) // This writes the client response
Should be 
err = t.ExecuteTemplate(w, &quot;TopMovies&quot;, p) // This writes the client response
  1. Incorrect context was passed to nested template

In the html file there was this code

{{define &quot;TopPicks&quot;}}
{{template &quot;header&quot; .}}
&lt;div class=&quot;content&quot;&gt;
{{.TopMovies}}
{{template &quot;MyTemplate&quot; . }}
&lt;/div&gt;
{{template &quot;footer&quot; .}}
{{end}}

Which should be

{{define &quot;TopPicks&quot;}}
{{template &quot;header&quot; .}}
&lt;div class=&quot;content&quot;&gt;
{{.TopMovies}}
{{template &quot;MyTemplate&quot; .TopMovies }}
&lt;/div&gt;
{{template &quot;footer&quot; .}}
{{end}}

The reason was that you were trying to pass the main context to the nested template, versus just the json result the template was expecting.

Original Answer

I made a simple example of what I think you need to do. Where I set the topMovies variable, this is where you would set the results from your api call. I hope this helps to show you the sequence you need to follow a little bit better.

package main
import (
&quot;net/http&quot;
&quot;text/template&quot;
)
type movie struct {
Title string
}
type page struct {
Title     string
TopMovies []movie
}
func main() {
http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, req *http.Request) {
w.Header().Add(&quot;Content Type&quot;, &quot;text/html&quot;)
templates := template.New(&quot;template&quot;)
templates.New(&quot;Body&quot;).Parse(doc)
templates.New(&quot;List&quot;).Parse(docList)
topMovies := []movie{{Title: &quot;Movie 1&quot;}, {Title: &quot;Movie 2&quot;}, {Title: &quot;Movie 3&quot;}}
page := page{Title: &quot;My Title&quot;, TopMovies: topMovies}
templates.Lookup(&quot;Body&quot;).Execute(w, page)
})
http.ListenAndServe(&quot;:8000&quot;, nil)
}
const docList = `
&lt;ul &gt;
{{range .}}
&lt;li&gt;{{.Title}}&lt;/li&gt;
{{end}}
&lt;/ul&gt;
`
const doc = `
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;&lt;title&gt;{{.Title}}&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Hello Templates&lt;/h1&gt;
{{template &quot;List&quot; .TopMovies}}
&lt;/body&gt;
&lt;/html&gt;
`

huangapple
  • 本文由 发表于 2016年3月12日 21:25:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/35958216.html
匿名

发表评论

匿名网友

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

确定