3

I've written goroutines that make API calls to Dropbox to get temporary image links to display on a personal website via golang templates. Dropbox is a little funny in that I must first make a request to get all images in the desired Dropbox folder and their specific paths. I then must make another call to Dropbox with the specific paths in the request body and Dropbox will respond with a temporary link to use.

So I have one go routine that that gets the list of image paths and sends them to another go routine via a channel. That second go routine then makes the request and gets the temporary link from DB which is then sent across another channel to a http handler func. Being that there could possibly be several hundreds of pictures, this process takes some time and the browser sits idle while the backend is making http requests. I would like to display a loading screen while golang is making all the http requests

I should note that the webpage itself is taking the images and making an automatic slideshow with them. I'm passing the links into the html using templates and "range" over them.

{{range .}}
    <img class="mySlides w3-animate-fading" src="{{.}}" style="width:100%">
{{end}}

First I attempted to just pass the final channel of links in the template and execute it that way, but the page would never render. It just sat there waiting to load.

err := templates.ExecuteTemplate(w, "index.html", LinkChannel)

The functionality that I did get to work is the following:

func homePage(w http.ResponseWriter, r *http.Request) {
    var links []string

    l := <-numOfImages

    //Iterate over the channel and append temporary links to list
    for link := range LinkChannel {
        links = append(links, link.Link)

        //Check if all images have been appended to break from loop
        if int64(len(links)) == l {
            break
        }
    }


    //Create a struct with the links to pass to html templates
    data := struct {
        Images []string
    }{Images: links}

    //Execute template with links
    err = templates.ExecuteTemplate(w, "index.html", data)
    check(err)
}

where l is the total number of images initially found by the first goroutine call to Dropbox. As you can see, I'm looping through the channel and waiting for all the links before serving index.html. As stated earlier since this looping can take time, the browser sits idle. I've tried adding another template execute statement before the loop

func homePage(w http.ResponseWriter, r *http.Request) {
    var links []string

    l := <-numOfImages

    err := templates.ExecuteTemplate(w, "load.html", nil)

    //Iterate over the channel and append temporary links to list
    for link := range LinkChannel {
        links = append(links, link.Link)

        //Check if all images have been appended to break from loop
        if int64(len(links)) == l {
            break
        }
    }


    //Create a struct with the links to pass to html templates
    data := struct {
        Images []string
    }{Images: links}

    //Execute template with links
    err = templates.ExecuteTemplate(w, "index.html", data)
    check(err)
}

But all this did was render a nasty frankenstein of the two html pages mixed together, and I still had to wait for the Dropbox calls before anything rendered. Is there a way to show a loading screen while waiting for the go routines? Or even better, can someone hopefully point me in the right direction of my first attempt, just passing in the channel to the template and iterating over it via templates so no loading screen is needed?

Moose Sims
  • 141
  • 2
  • 11
  • 4
    Template rendering happens at server side. HTTP is request-response based, which means if you want the client to do something while you work at the server side, you have to make multiple requests. First you could send the page with loading indicators, then make further AJAX request(s) to get the final URLs–which the client can then "insert" with JavaScript code. Or the server could send the now completely rendered HTML fragment, and the client can swap the placeholder in the DOM. – icza Jul 26 '19 at 09:07
  • Can you point me to an example demonstrating this? Or give an example? I'm not a full stack web developer. I'm more of a hacker that takes a bunch of examples of different things and get them to work in my desired effect. I know how to setup two different
    containers and make only one visible while content is loading and then make the other one visible after the content has loaded with JS. But this still doesnt fix my issue since golang isnt rendering the templates until after all requests to dropbox has been made
    – Moose Sims Jul 26 '19 at 19:27
  • The idea is that the first request to your server only sends back an HTML where there are only loading indicators, and it does not wait for your backend to finish all requests to dropbox. You launch a new goroutine to do that work, and you return from the handler. The client can display this page. Then the client makes further AJAX requests, maybe with the job ID which you may send in the first response, and the handler of those AJAX calls may wait for the goroutine launched in the previous request to finish, and send back the final URLs. The client can then process those final URLs. – icza Jul 26 '19 at 19:43
  • Can you script a toy box example of this for reference. I get what you're saying but not sure how to execute. Especially with regards to anything AJAX – Moose Sims Jul 26 '19 at 23:08
  • Here are some Go examples that use AJAX calls to modify the rendered page: [one](https://stackoverflow.com/questions/41136000/creating-load-more-button-in-golang-with-templates/41138344#41138344); [two](https://stackoverflow.com/questions/37118281/dynamically-refresh-a-part-of-the-template-when-a-variable-is-updated-golang/37119014#37119014); [three](https://stackoverflow.com/questions/29510296/how-do-i-implement-cascading-dropdown-using-golangs-templates/29512785#29512785). – icza Jul 26 '19 at 23:22

0 Answers0