Search code examples
gorandomgo-templates

Use Random Int for a Photo in Go Template


I have a simple random integer in my code that I am passing to the template

            min := 1
            max := 1563
            Photo := rand.Intn(max - min + 1)
            fmt.Println(Photo)

            tmpl.ExecuteTemplate(w, "index.html", struct {
                Pages        []Page
                CurrentPage  int
                TotalPage    int
                NextPage     int
                PreviousPage int
                LastPage     int
                ShowNext     bool
                ShowPrevious bool
                Photo        int
            }{

                Pages:        pages,
                CurrentPage:  pageIndex + 1,
                TotalPage:    totalPaginationPage,
                NextPage:     pageIndex + 1,
                PreviousPage: pageIndex - 1,
                LastPage:     totalPaginationPage - 1,
                ShowNext:     pageIndex+1 < totalPaginationPage,
                ShowPrevious: pageIndex-1 >= 0,
                Photo:        Photo,
            })

the idea is to randomize a photo (I have 1563 in the folder) so in my template

{{range .Pages}}

<div id="content">
  <div class="card">
    <p>
      <div class="card-img">

  
    
        <a href="{{.Slug}} ">    <img
        

        src="{{.Photo}}"
        alt=""
      /></a>
        </div>
        
        <div class="card-info">
          <div class="card-info-title">
<a href="{{.Slug}} " >{{.Title}} </a>
</div>

src="{{.Photo}}" crashes the template, is like if the variable is not properly passed. Perhaps the issue is that this is inside a loop so I need a random number per each article in order to display a photo ?

Is there any other way to do it directly in the template ?

Update

Thanks to the guidance I now have

min := 1
max := 1563
Photos := make([]int, len(pages))
for i := range Photos {
    Photos[i] = rand.Intn(max - min + 1)
}
            

            tmpl.ExecuteTemplate(w, "index.html", struct {
                Pages        []Page
                CurrentPage  int
                TotalPage    int
                NextPage     int
                PreviousPage int
                LastPage     int
                ShowNext     bool
                ShowPrevious bool
                Photo        []int
            }{

                Pages:        pages,
                CurrentPage:  pageIndex + 1,
                TotalPage:    totalPaginationPage,
                NextPage:     pageIndex + 1,
                PreviousPage: pageIndex - 1,
                LastPage:     totalPaginationPage - 1,
                ShowNext:     pageIndex+1 < totalPaginationPage,
                ShowPrevious: pageIndex-1 >= 0,
                Photo:        Photos,   
            })

and in the template

{{range $idx, $page := .Pages}}


<div id="content">
  <div class="card">
    <p>
      <div class="card-img">

  
    
        <a href="{{.Slug}} ">    <img
        
 src="{{index $.Photos $idx}}"
               alt=""
      /></a>
        </div>
        
        <div class="card-info">
          <div class="card-info-title">
<a href="{{.Slug}} " >{{.Title}} </a>
</div>

<div class="card-info-category">
  <p>

tags:
</p>
       
          <ul>
       
            <li>
  {{.Tags}}
          </li>
  

</ul>
</div>


<div class="card-info-date">
{{.Date}} 

</div>

</div>
</p>
</div>

</div>



       
{{end}} 

also tried

      <a href="{{.Slug}} ">    <img
        
 src="/public/suisse/suisse{{index $.Photos $idx}}.jpg"
               alt=""
      /></a>

but unfortunately the templates stop executing as soon as I call

{{index $.Photos $idx}} 

I assume it is a typo of some sort from my side?


Solution

  • The {{range}} action changes the dot, so {{.Photo}} inside the {{range .Pages}} will be resolved on the elements of .Pages.

    Use $ to refer to the "outer", original value passed to the template execution:

    src="{{$.Photo}}"
    

    Although this is only an integer, you likely want to use it in a path or an URL, something like this:

    src="/path/to/images?id={{$.Photo}}"
    

    Note: If you want a different image for all pages, you have to pass a different number for each, not just a single one. Then add a Photo field to page, and then you can refer to it inside the {{range}} as {{.Photo}} as in your original code.

    You wrote you can't modify Page as it comes from your database. If so, then either pass a slice of random numbers and access them with index like this:

    min := 1
    max := 1563
    Photos := make([]int, len(pages))
    for i := range Photos {
        Photos[i] = rand.Intn(max - min + 1)
    }
    
    tmpl.ExecuteTemplate(w, "index.html", struct {
        Pages        []Page
        CurrentPage  int
        TotalPage    int
        NextPage     int
        PreviousPage int
        LastPage     int
        ShowNext     bool
        ShowPrevious bool
        Photo        []int
    }{
    
        Pages:        pages,
        CurrentPage:  pageIndex + 1,
        TotalPage:    totalPaginationPage,
        NextPage:     pageIndex + 1,
        PreviousPage: pageIndex - 1,
        LastPage:     totalPaginationPage - 1,
        ShowNext:     pageIndex+1 < totalPaginationPage,
        ShowPrevious: pageIndex-1 >= 0,
        Photo:        Photos,
    })
    

    In template:

    {{range $idx, $page := .Pages}}
        <a href="{{.Slug}} "><img
            src="{{index $.Photos $idx}}"
            alt=""/>
        </a>
    {{end}}
    

    Or register a random function which you can call from the template:

    // Parse the template as you did, but first register a random function:
    min, max := 1, 1563
    tmpl, err := template.New("").Funcs(template.FuncMap{
        "random": func() int { return rand.Intn(max - min + 1) },
    }).ParseFiles("template-name.html")
    

    And you can call it from the template like this:

    {{range .Pages}}
        <a href="{{.Slug}} "><img
            src="{{random}}"
            alt=""/>
        </a>
    {{end}}