= A Dynamic Archive UI :chapter: 07 :url: /a-dynamic-archive-ui/ == A Dynamic Archive UI Contact.app has come a long way from a traditional web 1.0-style web application: we've added active search, bulk delete, some nice animations, and a slew of other features. We have reached a level of interactivity that most web developers would assume requires some sort of Single-Page Application JavaScript framework, but we've done it using htmx-powered hypermedia instead. Let's look at how we can add a final significant feature to Contact.app: downloading an archive of all the contacts. From a hypermedia perspective, downloading a file isn't exactly rocket science: using the HTTP `Content-Disposition` response header, we can easily tell the browser to download and save a file to the local computer. However, let's make this problem more interesting: let's add in the fact that the export can take a bit of time, from five to ten seconds, or sometimes even longer, to complete. This means that if we implemented the download as a "`normal`" HTTP request, driven by a link or a button, the user might sit with very little visual feedback, wondering if the download is actually happening, while the export is being completed. They might even give up in frustration and click the download hypermedia control again, causing a _second_ archive request. Not good. This turns out to be a classic problem in web app development. When faced with potentially long-running process like this, we ultimately have two options: * When the user triggers the action, block until it is complete and then respond with the result. * Begin the action and return immediately, showing some sort of UI indicating that things are in progress. Blocking and waiting for the action to complete is certainly the simpler way to handle it, but it can be a bad user experience, especially if the action takes a while to complete. If you've ever clicked on something in a web 1.0-style application and then had to sit there for what seems like an eternity before anything happens, you've seen the practical results of this choice. The second option, starting the action asynchronously (say, by creating a thread, or submitting it to a job runner system) is much nicer from a user experience perspective: the server can respond immediately and the user doesn't need to sit there wondering what's going on. But the question is, what do you respond _with_? The job probably isn't complete yet, so you can't provide a link to the results. We have seen a few different "`simple`" approaches in this scenario in various web applications: * Let the user know that the process has started and that they will be emailed a link to the completed process results when it is finished. * Let the user know that the process has started and recommend that they should manually refresh the page to see the status of the process. * Let the user know that the process has started and automatically refresh the page every few seconds using some JavaScript. All of these will work, but none of them is a great user experience. What we'd _really_ like in this scenario is something more like what you see when, for example, you download a large file via the browser: a nice progress bar indicating where in the process you are, and, when the process is complete, a link to click immediately to view the result of the process. This may sound like something impossible to implement with hypermedia, and, to be honest, we'll need to push htmx pretty hard to make this all work, but, when it is done, it won't be _that_ much code, and we will be able to achieve the user experience we want for this archiving feature. === UI Requirements Before we dive into the implementation, let's discuss in broad terms what our new UI should look like: we want a button in the application labeled "`Download Contact Archive.`" When a user clicks on that button, we want to replace that button with a UI that shows the progress of the archiving process, ideally with a progress bar. As the archive job makes progress, we want to move the progress bar along towards completion. Then, when the archive job is done, we want to show a link to the user to download the contact archive file. In order to actually do the archiving, we are going to use a python class, `Archiver`, that implements all the functionality that we need. As with the `Contact` class, we aren't going to go into the implementation details of `Archiver`, because that's beyond the scope of this book. For now you just need to know is that it provides all the server-side behavior necessary to start a contact archive process and get the results when that process is done. `Archiver` gives us the following methods to work with: * `status()` - A string representing the status of the download, either `Waiting`, `Running` or `Complete` * `progress()` - A number between 0 and 1, indicating how much progress the archive job has made * `run()` - Starts a new archive job (if the current status is `Waiting`) * `reset()` - Cancels the current archive job, if any, and resets to the "`Waiting`" state * `archive_file()` - The path to the archive file that has been created on the server, so we can send it to the client * `get()` - A class method that lets us get the Archiver for the current user A fairly uncomplicated API. The only somewhat tricky aspect to the whole API is that the `run()` method is _non-blocking_. This means that it does not _immediately_ create the archive file, but rather it starts a background job (as a thread) to do the actual archiving. This can be confusing if you aren't used to multithreading in code: you might be expecting the `run()` method to "`block`", that is, to actually execute the entire export and only return when it is finished. But, if it did that, we wouldn't be able to start the archive process and immediately render our desired archive progress UI. === Beginning Our Implementation We now have everything we need to begin implementing our UI: a reasonable outline of what it is going to look like, and the domain logic to support it. So, to start, note that this UI is largely self-contained: we want to replace the button with the download progress bar, and then the progress bar with a link to download the results of the completed archive process. The fact that our archive user interface is all going to be within a specific part of the UI is a strong hint that we will want to create a new template to handle it. Let's call this template `archive_ui.html`. Also note that we are going to want to replace the entire download UI in multiple cases: * When we start the download, we will want to replace the button with a progress bar. * As the archive process proceeds, we will want to replace/update the progress bar. * When the archive process completes, we will want to replace the progress bar with a download link. To update the UI in this way, we need to set a good target for the updates. So, let's wrap the entire UI in a `div` tag, and then use that `div` as the target for all our operations. Here is the start of the template for our new archive user interface: .Our initial archive UI template [source, html] ----