8

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("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) // 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, &p)
      if err != nil {
        panic(err)
      }

  // Following sends same information as above to the browser as 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) // This writes the client response
}

Then in main.go

   http.HandleFunc("/TopPicks", TopMoviesHandler)

TopPicks.html

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

What does work is this,

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

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

And in the html template

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

How can I make this work for my json response?

2 Answers 2

3

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

I think you want: t.ExecuteTemplate(w, "body", 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 "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")

    // parse all templates you may want
    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)
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Hey man you were absolutely right... by changing it to execute body it does indeed put the data in the page. But it overwrites the whole page. My goal here is to take that data and have it display in the html template page with the header and navigation bar. is there no way to call the data from the html page in the correct location?
Yes sir it did. Thank you. I will upvote it now. Would you mind taking a look at this one? stackoverflow.com/q/35978605/2218253 I appreciate you very much David. My only problem now is building a url for a search function.
1

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, "T", p) // This writes the client response

Should be 

err = t.ExecuteTemplate(w, "TopMovies", p) // This writes the client response
  1. Incorrect context was passed to nested template

In the html file there was this code

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

Which should be

{{define "TopPicks"}}
     {{template "header" .}}
     <div class="content">
          {{.TopMovies}}
          {{template "MyTemplate" .TopMovies }}
     </div>
     {{template "footer" .}}
{{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 (
    "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>
`

10 Comments

I have seen an example very similar to this but unfortunately it does not get me there. The problem is more directed in that I cant manage my json response to the html page. I was able to get the response to display on the initial html page but once I created a navigation and multiple templates I have yet to be able to get the response to display on the specific page I need.
Actually The answer above this did get me closer. The data displays now but not the way I need it to. if you see my recent comment I just made it describes my problem. I think I just need to know how to call the data from the page instead of the current function.
Well, one thing I see and missed earlier is that in the topPicksHandler, shouldn't err = t.ExecuteTemplate(w, "T", p) // This writes the client response be err = t.ExecuteTemplate(w, "Topmovies", p) ?
If you check my question out in the section I show how my html page is set up you will see what I mean. I have a header.html footer.html etc. I am calling the templates in different pages as needed. So my goal here is to create a TopPicks/html template so I can call it when needed.
You should call the template like this I believe {{template "My Template" .TopPicks }} Otherwise I think you would need to change the nested template to use {{ range .TopPIcks.Results }}
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.