Building Dynamic Web Applications with Python primer
Posted on Fri 18 September 2020 in python
If you are a full-stack developer, whether you like it or not, you are going to use Javascript. It's an incredible language that allows developers to create dynamic applications. The amount of Javascript you use is up to you. Javascript can be used to power an entire application or it can be used to add dynamic elements to a web page.
In a typical python web application, the page the user sees in the browser is HTML provided by the server. This method allows for static pages. The content on the page can only be updated by requesting the entire page from the server again. Alone, this is not a suitable method for creating dynamic frontend experiences for the user.
To make more dynamic pages, you will typically need to use Javascript. As you start to look to improve the user experience, Javascript becomes more and more necessary. Javascript frameworks will begin to look appealing. Javascript frameworks vary widely in their functionality and approach to solving problems. Minimal frameworks such as Stimulusjs and AlpineJS allow you to do a lot of the work using custom properties in HTML. On the other end of the spectrum are full-blown frontend frameworks that use the server only as an API to store and request data.
Looking at Javascript frameworks as a whole, we can learn that keeping track of the state and properties of the application is a big problem and each of these frameworks have their own way of solving it.
After using a variety of frameworks I have recently been exploring ways to use server-rendered HTML to add dynamic elements to a page. Most recently this includes using a combination of Stimulus and Alpine with Python. In this context, Stimulus is used to fetch HTML that is rendered server-side and is then swapped out as necessary. An example on the Stimulus site that shows how easy this is to do.
Small HTML components allow for content to be loaded into the DOM at any time using data attributes.
# Here is an example of a container that will
# be picked up by the Stimulus controller
<div data-controller="content-loader"
data-content-loader-url="/messages"></div>
The component uses data attributes to define which Stimulus controller is used. When the page loads the controller connect()
function runs and fetches HTML from the server and loads it into the component.
// Stimulus detects a controller in the HTML and runs the "content-loader" function.
// This function will make a request to the server. The server will generate
// the appropriate HTML and return it. The HTML from the server is then
// injected into the DOM when the request returns from the server.
// src/controllers/content_loader_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
connect() {
this.load()
}
load() {
fetch(this.data.get("url"))
.then(response => response.text())
.then(html => {
this.element.innerHTML = html
})
}
}
This same model can be used with Alpine as well. Here is an example from the documentation that fetches HTML from the server when mousing over a button.
<div x-data="{ open: false }">
<button
@mouseenter.once="
fetch('/dropdown-partial.html')
.then(response => response.text())
.then(html => { $refs.dropdown.innerHTML = html })
"
@click="open = true"
>Show Dropdown</button>
<div x-ref="dropdown" x-show="open" @click.away="open = false">
Loading Spinner...
</div>
</div>
Part of this workflow that could use some improvement. A new endpoint on the needs to be created on the server for each component. I am looking into ways to solve this. Currently, the component endpoints are stored in the same file as the CRUD routes. This begins to feel overwhelming as more components are added. I'm currently trying out a couple of different options will make another post if anything notable is found.