Skip to content

Instantly share code, notes, and snippets.

@YangSiJun528
Last active December 20, 2025 10:48
Show Gist options
  • Select an option

  • Save YangSiJun528/4682b1346fa3eb6316e78f2f37554873 to your computer and use it in GitHub Desktop.

Select an option

Save YangSiJun528/4682b1346fa3eb6316e78f2f37554873 to your computer and use it in GitHub Desktop.
Hypermedia Systems 책 웹페이지 버전 하나로 합치는 파이썬 스크립트

찾은 곳: https://news.hada.io/topic?id=25182 댓글에서 재밌어보여서 읽으려는데, 읽기가 불편해서 하나로 합치는거 AI로 만듦. 나중에 사이트 내용 바뀌면 다시 읽으려고 만듦.

gistcdn.githack.com 같은거 써서 바로 웹 html로 볼 수 있음. 링크

아래는 AI한테 줄 컨텍스트? 임


Hypermedia Systems Book Downloader

이 프로젝트는 Hypermedia Systems 웹사이트에서 제공하는 책을 하나의 HTML 파일로 다운로드하고 병합하는 도구입니다.

프로젝트 목적

Hypermedia Systems 책은 웹페이지로 제공되지만, 목차에서 각 챕터로 이동하는 링크만 있어 연속해서 읽기가 불편합니다. 이 도구는 모든 챕터를 하나의 HTML 파일로 병합하여 더 편리하게 읽을 수 있도록 합니다.

작동 원리

  1. 목차 페이지 분석: https://hypermedia.systems/book/contents/ 에서 모든 챕터 링크를 추출합니다.
  2. 챕터 다운로드: 각 챕터의 HTML 콘텐츠를 순차적으로 다운로드합니다.
  3. 콘텐츠 병합: 모든 챕터를 하나의 HTML 파일로 병합하고, 읽기 좋은 스타일을 적용합니다.

사용 방법

1. 가상 환경 생성 (권장)

python3 -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

2. 필요한 패키지 설치

pip install -r requirements.txt

3. 스크립트 실행

python download_book.py

4. 결과 확인

hypermedia_systems_complete.html 파일이 생성됩니다. 이 파일을 브라우저에서 열어서 전체 책을 읽을 수 있습니다.

5. 가상 환경 종료 (선택)

deactivate

책 구조 (2025년 12월 기준)

현재 책은 다음 18개의 챕터로 구성되어 있습니다:

  1. Copyright & Acknowledgments
  2. Dedication
  3. Foreword
  4. Introduction
  5. Hypermedia: A Reintroduction
  6. Components Of A Hypermedia System
  7. A Web 1.0 Application
  8. Extending HTML As Hypermedia
  9. Htmx Patterns
  10. More Htmx Patterns
  11. A Dynamic Archive UI
  12. Tricks Of The Htmx Masters
  13. Client Side Scripting
  14. JSON Data APIs
  15. Hyperview: A Mobile Hypermedia
  16. Building A Contacts App With Hyperview
  17. Extending The Hyperview Client
  18. Conclusion

AI 에이전트를 위한 컨텍스트

이 섹션은 나중에 AI가 이 프로젝트를 유지보수할 때 필요한 정보를 담고 있습니다.

사이트 구조 분석 방법

  1. 목차 페이지 확인:

    https://hypermedia.systems/book/contents/
    

    이 페이지에서 모든 챕터 링크를 추출합니다.

  2. 챕터 URL 패턴:

    • 기본 URL: https://hypermedia.systems
    • 챕터 경로: /chapter-name/ (예: /introduction/, /hypermedia-a-reintroduction/)
  3. HTML 구조:

    • 주요 콘텐츠는 <main>, <article>, 또는 .content 클래스를 가진 <div> 태그 안에 있습니다.
    • 사이트 구조가 변경될 경우, download_book.py의 HTML 파싱 로직을 수정해야 합니다.

업데이트가 필요한 경우

1. 새로운 챕터가 추가된 경우

download_book.pychapters 리스트에 새 챕터를 추가합니다:

chapters = [
    # ... 기존 챕터들
    ("/new-chapter-path/", "New Chapter Title"),
]

2. 사이트 UI가 변경된 경우

HTML 구조가 변경되면 download_book.py의 다음 부분을 수정합니다:

# 현재 코드
main_content = soup.find('main') or soup.find('article') or soup.find('div', class_='content')

# 새로운 HTML 구조에 맞게 선택자 수정
main_content = soup.find('새로운_태그') or soup.find('새로운_클래스')

3. 스타일 개선

html_template<style> 섹션을 수정하여 더 나은 가독성을 제공할 수 있습니다.

트러블슈팅

다운로드 실패

  • 원인: 네트워크 오류, 사이트 접근 제한
  • 해결: download_book.pytime.sleep(0.5) 값을 늘려서 요청 간격을 증가시킵니다.

HTML 파싱 오류

  • 원인: 사이트 구조 변경
  • 해결:
    1. 브라우저에서 챕터 페이지를 열고 개발자 도구로 HTML 구조를 확인합니다.
    2. soup.find() 호출을 새로운 구조에 맞게 수정합니다.

인코딩 문제

  • 원인: 특수 문자나 다국어 콘텐츠
  • 해결: open() 함수에 encoding='utf-8' 파라미터가 지정되어 있는지 확인합니다.

자동화 작업 체크리스트

AI가 이 프로젝트를 업데이트할 때 수행해야 할 작업:

  1. 목차 페이지에서 최신 챕터 리스트 확인
  2. chapters 리스트가 최신 상태인지 확인
  3. 테스트 다운로드 실행 (1-2개 챕터만)
  4. HTML 파싱이 정상 작동하는지 확인
  5. 전체 다운로드 실행
  6. 생성된 HTML 파일의 품질 확인
  7. README 업데이트 (필요한 경우)

기술 스택

  • Python 3.x: 메인 프로그래밍 언어
  • requests: HTTP 요청 처리
  • BeautifulSoup4: HTML 파싱 및 조작
  • time: 요청 간격 제어 (rate limiting)

라이선스 및 저작권

이 도구는 개인적인 학습 목적으로 만들어졌습니다. Hypermedia Systems 책의 저작권은 원저자에게 있으며, 이 도구는 책의 콘텐츠를 재배포하지 않고 단순히 웹에서 제공되는 내용을 다운로드하고 병합하는 기능만 제공합니다.

참고 사항

  • 웹사이트에 과도한 부하를 주지 않기 위해 요청 사이에 0.5초의 지연을 두고 있습니다.
  • 생성된 HTML 파일은 로컬에서만 사용하시기 바랍니다.
  • 원본 사이트가 업데이트되면 이 도구를 다시 실행하여 최신 버전을 다운로드할 수 있습니다.
#!/usr/bin/env python3
"""
Hypermedia Systems Book Downloader
이 스크립트는 hypermedia.systems 웹사이트에서 모든 챕터를 다운로드하고
하나의 HTML 파일로 병합합니다.
"""
import requests
from bs4 import BeautifulSoup
import time
import sys
# Base URL
BASE_URL = "https://hypermedia.systems"
# Chapter paths - 이 리스트를 업데이트하여 새로운 챕터를 추가하거나 제거할 수 있습니다
CHAPTERS = [
("/copy-ack/", "Copyright & Acknowledgments"),
("/dedication/", "Dedication"),
("/foreword/", "Foreword"),
("/introduction/", "Introduction"),
("/hypermedia-a-reintroduction/", "Hypermedia: A Reintroduction"),
("/components-of-a-hypermedia-system/", "Components Of A Hypermedia System"),
("/a-web-1-0-application/", "A Web 1.0 Application"),
("/extending-html-as-hypermedia/", "Extending HTML As Hypermedia"),
("/htmx-patterns/", "Htmx Patterns"),
("/more-htmx-patterns/", "More Htmx Patterns"),
("/a-dynamic-archive-ui/", "A Dynamic Archive UI"),
("/tricks-of-the-htmx-masters/", "Tricks Of The Htmx Masters"),
("/client-side-scripting/", "Client Side Scripting"),
("/json-data-apis/", "JSON Data APIs"),
("/hyperview-a-mobile-hypermedia/", "Hyperview: A Mobile Hypermedia"),
("/building-a-contacts-app-with-hyperview/", "Building A Contacts App With Hyperview"),
("/extending-the-hyperview-client/", "Extending The Hyperview Client"),
("/conclusion/", "Conclusion"),
]
# HTML template for the combined book
HTML_TEMPLATE = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hypermedia Systems - Complete Book</title>
<style>
body {{
max-width: 900px;
margin: 0 auto;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #fff;
}}
.chapter {{
margin-bottom: 60px;
page-break-after: always;
}}
.chapter-title {{
font-size: 2em;
margin-top: 40px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #333;
color: #000;
}}
h1, h2, h3, h4, h5, h6 {{
margin-top: 1.5em;
margin-bottom: 0.5em;
color: #000;
}}
p {{
margin-bottom: 1em;
}}
pre {{
background: #f5f5f5;
padding: 15px;
overflow-x: auto;
border-radius: 5px;
border: 1px solid #ddd;
}}
code {{
background: #f5f5f5;
padding: 2px 5px;
border-radius: 3px;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
font-size: 0.9em;
}}
pre code {{
background: transparent;
padding: 0;
}}
img {{
max-width: 100%;
height: auto;
display: block;
margin: 20px auto;
}}
blockquote {{
border-left: 4px solid #ddd;
padding-left: 20px;
margin-left: 0;
color: #666;
font-style: italic;
}}
table {{
border-collapse: collapse;
width: 100%;
margin: 20px 0;
}}
th, td {{
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}}
th {{
background-color: #f5f5f5;
font-weight: bold;
}}
a {{
color: #0066cc;
text-decoration: none;
}}
a:hover {{
text-decoration: underline;
}}
.book-title {{
text-align: center;
font-size: 3em;
margin-bottom: 10px;
margin-top: 20px;
}}
.book-subtitle {{
text-align: center;
color: #666;
margin-bottom: 50px;
}}
@media print {{
body {{
max-width: 100%;
}}
.chapter {{
page-break-after: always;
}}
}}
</style>
</head>
<body>
<h1 class="book-title">Hypermedia Systems</h1>
<p class="book-subtitle">Downloaded from hypermedia.systems</p>
{chapters}
</body>
</html>
"""
def download_chapter(url, title):
"""
단일 챕터를 다운로드하고 주요 콘텐츠를 추출합니다.
Args:
url: 챕터의 전체 URL
title: 챕터 제목
Returns:
챕터의 HTML 콘텐츠 (문자열)
"""
try:
response = requests.get(url, timeout=30)
response.raise_for_status()
# Parse the HTML
soup = BeautifulSoup(response.text, 'html.parser')
# 주요 콘텐츠 영역 찾기
# 사이트 구조가 변경되면 이 부분을 수정해야 할 수 있습니다
main_content = (
soup.find('main') or
soup.find('article') or
soup.find('div', class_='content') or
soup.find('div', id='content')
)
if main_content:
# 상대 경로 이미지를 절대 경로로 변환
for img in main_content.find_all('img'):
if img.get('src') and not img['src'].startswith(('http://', 'https://', '//')):
if img['src'].startswith('/'):
img['src'] = BASE_URL + img['src']
else:
img['src'] = url.rsplit('/', 1)[0] + '/' + img['src']
# 상대 경로 링크를 절대 경로로 변환
for link in main_content.find_all('a', href=True):
if not link['href'].startswith(('http://', 'https://', '//', '#', 'mailto:')):
if link['href'].startswith('/'):
link['href'] = BASE_URL + link['href']
else:
link['href'] = url.rsplit('/', 1)[0] + '/' + link['href']
return str(main_content)
else:
# fallback: body 태그 사용
body = soup.find('body')
return str(body) if body else response.text
except requests.RequestException as e:
print(f" ⚠️ Error downloading {title}: {e}", file=sys.stderr)
return f'<div class="error"><p>Error downloading this chapter: {e}</p></div>'
except Exception as e:
print(f" ⚠️ Unexpected error for {title}: {e}", file=sys.stderr)
return f'<div class="error"><p>Unexpected error: {e}</p></div>'
def main():
"""
메인 함수: 모든 챕터를 다운로드하고 병합합니다.
"""
print("=" * 70)
print("Hypermedia Systems Book Downloader")
print("=" * 70)
print(f"\nTotal chapters to download: {len(CHAPTERS)}\n")
chapter_contents = []
# 각 챕터 다운로드
for i, (path, title) in enumerate(CHAPTERS, 1):
url = BASE_URL + path
print(f"[{i}/{len(CHAPTERS)}] Downloading: {title}")
content = download_chapter(url, title)
chapter_contents.append({
'title': title,
'content': content
})
# 서버에 부담을 주지 않기 위한 지연
if i < len(CHAPTERS): # 마지막 챕터 후에는 대기하지 않음
time.sleep(0.5)
# 챕터들을 HTML로 병합
print("\n" + "=" * 70)
print("Merging chapters into single HTML file...")
print("=" * 70)
chapters_html = ""
for chapter in chapter_contents:
chapters_html += f"""
<div class="chapter">
<h2 class="chapter-title">{chapter['title']}</h2>
{chapter['content']}
</div>
"""
# 최종 HTML 생성
final_html = HTML_TEMPLATE.format(chapters=chapters_html)
# 파일 저장
output_file = "hypermedia_systems_complete.html"
try:
with open(output_file, 'w', encoding='utf-8') as f:
f.write(final_html)
print(f"\n✅ Success! Combined book saved to: {output_file}")
print(f"📚 Total chapters included: {len(chapter_contents)}")
print(f"📄 File size: {len(final_html):,} characters")
print("\nYou can now open the HTML file in your browser to read the complete book.")
except IOError as e:
print(f"\n❌ Error writing file: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n⚠️ Download interrupted by user.")
sys.exit(1)
except Exception as e:
print(f"\n❌ Unexpected error: {e}", file=sys.stderr)
sys.exit(1)
This file has been truncated, but you can view the full file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hypermedia Systems - Complete Book</title>
<style>
body {
max-width: 900px;
margin: 0 auto;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #fff;
}
.chapter {
margin-bottom: 60px;
page-break-after: always;
}
.chapter-title {
font-size: 2em;
margin-top: 40px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #333;
color: #000;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.5em;
margin-bottom: 0.5em;
color: #000;
}
p {
margin-bottom: 1em;
}
pre {
background: #f5f5f5;
padding: 15px;
overflow-x: auto;
border-radius: 5px;
border: 1px solid #ddd;
}
code {
background: #f5f5f5;
padding: 2px 5px;
border-radius: 3px;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
font-size: 0.9em;
}
pre code {
background: transparent;
padding: 0;
}
img {
max-width: 100%;
height: auto;
display: block;
margin: 20px auto;
}
blockquote {
border-left: 4px solid #ddd;
padding-left: 20px;
margin-left: 0;
color: #666;
font-style: italic;
}
table {
border-collapse: collapse;
width: 100%;
margin: 20px 0;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f5f5f5;
font-weight: bold;
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.book-title {
text-align: center;
font-size: 3em;
margin-bottom: 10px;
margin-top: 20px;
}
.book-subtitle {
text-align: center;
color: #666;
margin-bottom: 50px;
}
@media print {
body {
max-width: 100%;
}
.chapter {
page-break-after: always;
}
}
</style>
</head>
<body>
<h1 class="book-title">Hypermedia Systems</h1>
<p class="book-subtitle">Downloaded from hypermedia.systems</p>
<div class="chapter">
<h2 class="chapter-title">Copyright & Acknowledgments</h2>
<main>
<p><strong>Hypermedia Systems</strong><br/>
© 2023 Carson Gross, Adam Stepinski, Deniz Akşimşek</p>
<p>Editor: William Talcott</p>
<p>CC BY-NC-SA: This work is licensed under the Creative Commons
Attribution-NonCommercial-ShareAlike 4.0 International License. To view
a copy of this license, visit <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">http://creativecommons.org/licenses/by-nc-sa/4.0/</a>
or send a letter to Creative Commons, PO Box 1866, Mountain View, CA
94042, USA.</p>
<p>Read online: <a href="https://hypermedia.systems/">https://hypermedia.systems/</a></p>
<p>Thanks to:</p>
<ul>
<li><p>Berkeley Graphics for their permission to use <em>Berkeley
Mono</em> and the design of the first edition’s cover,</p></li>
<li><p>All contributors to the book: (<a href="https://github.com/bigskysoftware/hypermedia-systems-book/graphs/contributors">https://github.com/bigskysoftware/hypermedia-systems-book/graphs/contributors</a>),</p></li>
<li><p>Manning Publications for their support in the initial stages of
writing this book,</p></li>
<li><p>India Hackle for her valuable feedback and editing.</p></li>
</ul>
<p>Typefaces:</p>
<ul>
<li><p>Libertinus Serif (by its authors: <a href="https://github.com/alerque/libertinus/blob/master/CONTRIBUTORS.txt,">https://github.com/alerque/libertinus/blob/master/CONTRIBUTORS.txt,</a>
for body text),</p></li>
<li><p>Libertinus Sans (by the same, for secondary text),</p></li>
<li><p>Berkeley Mono (by Berkeley Graphics, for code and ascii art
diagrams).</p></li>
<li><p>Jaro (by Agyei Archer, Celine Hurka, and Mirko Velimirović, for
headings)</p></li>
</ul>
<p>Written and typeset with Typst (<a href="https://typst.app">https://typst.app</a>).</p>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Dedication</h2>
<main>
<div data-align="horizon">
<p><em>To my family and the htmx discord.</em> — Carson Gross</p>
<p><em>To my wife Tarunya, for her support through the ups and downs of
this project.</em> — Adam Stepinski</p>
<p><em>Annem Lamia Akşimşek ve babam Özgür Akşimşek’e.</em> — Deniz
Akşimşek</p>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Foreword</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
</details>
<div class="division-content">
<h0>Foreword</h0>
<p>While there have been many books on the topic of hypermedia, there is
a select number of publications that chronicle important advances in the
field of hypermedia and this book is one of them. Not only does this
book describe the benefits of creating hypermedia-driven applications
(HDAs), it leads the reader through working and practical examples of
how to do just that. And, in doing so, the authors (Gross, Stepinski,
and Akşimşek) call out contributions from important figures in the
history of hypermedia systems. And, as of this writing, that history
spans more than half a century.</p>
<p>In 1974, Ted Nelson’s <em class="test">“Computer Lib/Machine Dreams”</em> marked
the start of the modern hypermedia era with a book that Steven Levy
(author of <em class="test">“Hackers”</em>) described as “the epic of the computer
revolution.” Nelson is credited with coining the terms HyperText,
HyperLink, HyperMedia, and HyperData as well as Intertwingularity; the
notion that all information is connected — both intertwined and
intermingled. Almost half a century ago, he foretold a future where any
person could publish anything anytime without the need for permission
from any central controlling source. And his hyperlinks were the engine
of that future.</p>
<p>It took two decades before Nelson’s idea of intertwingled computing
became widespread. Along the way, Douglas Engelbart created the
<em class="test">oN-Line System</em> or NLS, Wendy Hall built the <em class="test">Microcosm</em>,
and, eventually, in the 1980s, Tim Berners-Lee defined the World Wide
Web (WWW), HTML, and HTTP. It was Berners-Lee’s iteration that has
become the backbone and the standard for the intertwingularity we all
experience today.</p>
<p>By the year 2000, the technical foundations of “the web” were
documented in Roy Fielding’s PhD dissertation <em class="test">“Architectural Styles
and the Design of Network-based Software Architectures”</em>). In that
work, Fielding defined the architectural model of <em class="test">REpresentational
State Transfer</em> or REST. This set of system properties and
implementation constraints have proven — even a quarter-century later —
to be a reliable model for designing and building the intertwingled
machines that today affect billions of people around the globe.</p>
<p>Even though Fielding’s work was important, it wasn’t until Leonard
Richardson and Sam Ruby published <em class="test">“RESTful Web Services”</em> in
2008 that the REST model became well-known to the world of software
architecture and development. Backed by the Ruby programming platform,
the ideas behind Fielding’s REST model became <em class="test">de rigueur</em> for
the creation of web-based services and client applications.</p>
<p>One of the reasons Richardson and Ruby’s work was so important was
that, unlike dissertations and futuristic predictions, the <em class="test">RESTful
Web Services</em> book outlined a practical working framework for
building powerful applications for the Web. It described not only the
power of REST but also provided step-by-step instructions on how to
build them. Richardson and Ruby brought together the hypermedia
scholarship of the previous twenty years all in one place.</p>
<p>And now we can add this book (<em class="test">“Hypermedia Systems”</em>) to that
list of important works. From the book’s introduction through the
step-by-step directions on how to use HTMX for browsers and Hyperview
for mobile devices, the authors describe the benefits of creating
hypermedia-driven applications (HDAs). They also offer dozens of
practical working examples the reader can use right away in building
their own hypermedia solutions.</p>
<p>I’ve been working in the field of hypermedia for close to thirty
years and have seen quite a few books, papers, dissertations, and
programming platforms come and go in that time. Occasionally, one of
these works “nails it” — provides the right mix of theory and practice
delivered in a way that helps readers make a connection between their
own efforts and the activities of the community at large. I am happy to
say that this book is one of those works. The authors have not only
created powerful tooling in HTMX and Hyperview, they have also advanced
the notion of hypermedia systems and hypermedia-driven applications in
ways that a wide audience can understand and apply.</p>
<p>Nelson describes a future where the barriers to publishing and data
sharing are lowered and the creative energies of the world are easily
shared and applied. This is neither a new or unique idea but one that
does need continual renewal and encouragement. Nelson saw his hyperlink
and hypermedia as the driving force for intertwingularity between people
and machines around the world. In this idea alone, hypermedia is a
powerful approach to creating computer systems that enable people to
work together for the common good. As this book’s authors say,
“Hypermedia was a great idea! It still is!”</p>
<p><em class="test">Mike Amundsen, April 2023</em></p>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Introduction</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/introduction/#what-is-a-hypermedia-system">What is a Hypermedia System?</a>
</li><li>
<a href="https://hypermedia.systems/introduction/#hypermedia-driven-applications">Hypermedia-Driven Applications</a>
</li><li>
<a href="https://hypermedia.systems/introduction/#goals">Goals</a>
</li><li>
<a href="https://hypermedia.systems/introduction/#book-layout">Book Layout</a>
</li><li>
<a href="https://hypermedia.systems/introduction/#hypermedia--a-new-generation">Hypermedia: A New Generation</a>
</li><li>
<a href="https://hypermedia.systems/introduction/#html-note-title">HTML Notes: Hypermedia In Practice</a>
</li></ul>
</details>
<div class="division-content">
<p>This is a book about building applications using hypermedia systems.
<em class="test">Hypermedia systems</em> might seem like a strange phrase: how is
hypermedia a <em class="test">system</em>? Isn’t hypermedia just a way to link
documents together?</p>
<p>Like with HTML, on the World Wide Web?</p>
<p>What do you mean hypermedia <em class="test">systems</em>?</p>
<p>Well, yes, HTML is <em class="test">a</em> hypermedia. But there is more to the
way the web works than just HTML: HTTP, the Hyper Text Transfer
Protocol, is what transfers HTML from servers to clients, and there are
many details and features associated with it: caching, various headers,
response codes, and so forth.</p>
<p>And then, of course, there are <em class="test">hypermedia servers</em>, which
present <em class="test">hypermedia APIs</em> (yes, <em class="test">APIs</em>) to clients over
the network.</p>
<p>And, finally, there is the all-important <em class="test">hypermedia client</em>:
a software client that understands how to render a <em class="test">hypermedia
response</em> intelligibly to a human, so that a human can interact with
the remote system. The most widely known and used hypermedia clients
are, of course, web browsers.</p>
<p>Web browsers are perhaps the most sophisticated pieces of software we
use. They not only understand HTML, CSS and many other file formats, but
they also provide a JavaScript runtime and programming environment that
is so powerful that web developers can create entire applications in it
that are nearly as sophisticated as <em class="test">thick clients</em>, that is,
native applications.</p>
<p>This JavaScript runtime is so powerful, in fact, that today many
developers ignore the <em class="test">hypermedia</em> features of the browser, in
favor of building their web applications entirely in JavaScript.
Applications built in this manner have come to be called Single Page
Applications (SPAs). Rather than navigating between pages, these web
applications use JavaScript for updating the user interface directly.
When they communicate with a server, these applications typically use
JSON API calls via AJAX. And they often update the user interface using
a “reactive” style frontend JavaScript library.</p>
<p>In these applications HTML becomes a (somewhat awkward) graphical
interface description language that is used because, for historical
reasons, that’s what happens to be there, in the browser.</p>
<p>Applications built in this style are not <em class="test">hypermedia-driven</em>:
they do not take advantage of the underlying hypermedia system of the
web.</p>
<p>To explain what a hypermedia-driven application looks like, and to
contrast it with the popular SPA approach of today, we need to first
explore the entire <em class="test">hypermedia system</em> of the web, beyond just
discussing HTML. We need to look at the <em class="test">network architecture</em> of
the web, including how a web server delivers a hypermedia API, and how
to effectively use the hypermedia features available in the hypermedia
<em class="test">client</em> (e.g., the browser).</p>
<p>Each of these are important aspects of building an effective
hypermedia-driven application, and it is the entire <em class="test">hypermedia
system</em> that comes together to make hypermedia such a powerful
architecture.</p>
<h2 id="what-is-a-hypermedia-system">What is a Hypermedia System?</h2>
<p>To understand what a hypermedia system is we’ll first take an
in-depth look at <em class="test">the</em> canonical hypermedia system: the World
Wide Web. Roy Fielding, an engineer who helped create specifications and
build the implementations of many early pieces of the web, gave us the
term REpresentational State Transfer, or REST. In his PhD dissertation
he described REST as a <em class="test">network architecture</em>, and he contrasted
it with earlier approaches to building distributed software.</p>
<p>We define a <em class="test">hypermedia system</em> as a system that adheres to
the RESTful network architecture in Fielding’s <em class="test">original</em> sense
of this term.</p>
<p>Unfortunately, today, you probably associate the term “REST” with
JSON APIs, since that is where the term is typically used in industry.
This is a misapplied use of the term REST because JSON is not a
<em class="test">natural</em> hypermedia due to the absence of hypermedia controls.
The exchange of hypermedia is an explicit requirement for a system to be
considered “RESTful.” It is a long story how we got here, using the term
REST so incorrectly, and we will go into the details later in this book.
But, for now, if you think REST implies JSON, please try to set that
understanding aside while reading this book, and come to the concept
with fresh eyes.</p>
<p>It is important to understand that, in his dissertation, Fielding was
describing The World Wide Web as it existed in the late 1990s. The web,
at that point, was simply web browsers exchanging hypermedia. That
system, with its simple links and forms, was what Fielding was calling
RESTful.</p>
<p>JSON APIs were a decade away from becoming a common tool in web
development: REST was about <em class="test">hypermedia</em> and the 1.0 version of
the web.</p>
<h2 id="hypermedia-driven-applications">Hypermedia-Driven Applications</h2>
<p>In this book we are going to take a look at hypermedia as a
<em class="test">system architecture</em> and then explore some practical,
<em class="test">modern</em> approaches to building web applications using it. We
will call applications built in this style <em class="test">Hypermedia-Driven
Applications</em>, or HDAs, and we contrast them with a popular style in
use today, the Single Page Application.</p>
<p>A Hypermedia-Driven Application is an application built on top of a
hypermedia system that respects and utilizes the hypermedia
functionality of that underlying system.</p>
<h2 id="goals">Goals</h2>
<p>The goal of this book is to give you a strong sense of how the
RESTful, hypermedia system architecture <em class="test">differs</em> from other
client-server systems, and what the strengths (and weaknesses) of the
hypermedia approach are. Further, we hope to convince you that the
hypermedia architecture is <em class="test">relevant</em> to developers building
modern web applications.</p>
<p>We aim to give you the tools to evaluate the requirements for an
application and answer the question:</p>
<p>“Could I build this as a Hypermedia-Driven Application?”</p>
<p>We hope that for many applications the answer to that question will
be “Yes!”</p>
<h2 id="book-layout">Book Layout</h2>
<p>The book is broken into three parts:</p>
<ul>
<li><p>An introduction (or re-introduction) to hypermedia, with a
particular focus on HTML and HTTP. We will finish this review of core
hypermedia concepts by creating a simple “Web 1.0“-style application,
Contact.app, for managing contacts.</p></li>
<li><p>Next we will look at how we can use <a href="https://htmx.org">htmx</a>, a hypermedia-oriented JavaScript
library created by the authors of this book, to improve Contact.app. By
using htmx, we will be able to achieve a level of interactivity in our
application that many developers would expect to require a large,
sophisticated front end library, such as React. Thanks to htmx, we will
be able to do this using hypermedia as our system architecture.</p></li>
<li><p>Finally, we will look at a completely different hypermedia
system, Hyperview. Hyperview is a <em class="test">mobile</em> hypermedia system,
related to, but distinct from the web and created by one of the authors
of this book – Adam Stepinski. It supports <em class="test">mobile specific</em>
features by providing not only a mobile specific hypermedia, but also a
mobile hypermedia client. These novel components, combined with any HTTP
server, make it possible to build mobile Hypermedia-Driven
Applications.</p></li>
</ul>
<p>Note that each section is <em class="test">somewhat</em> independent of the
others. If you already know hypermedia in-depth and how basic Web 1.0
applications function, you may want to skip ahead to the second section
on htmx and how to build modern web applications using hypermedia.
Similarly, if you are well versed in htmx and want to dive into a novel
<em class="test">mobile</em> hypermedia, you can skip ahead to the Hyperview
section.</p>
<p>That being said, the book is designed to be read in order and both
the htmx and Hyperview sections build on the Web 1.0 application
described at the end of the first section. Furthermore, even if you
<em class="test">are</em> well versed in all the concepts of hypermedia and details
of HTML &amp; HTTP, it is likely worth it to at least skim through the
first few chapters for a refresher.</p>
<h2 id="hypermedia--a-new-generation">Hypermedia: A New Generation</h2>
<p>Hypermedia isn’t a frequent topic of discussion these days. Even many
older programmers who grew up with the web in the late 1990s and early
2000s haven’t thought much about these ideas in years. Many younger web
developers have grown up knowing nothing but Single Page Applications
and the frameworks that are used to build them.</p>
<p>In particular, many young web developers began their careers by
building React.js applications that interact with a Node server using a
JSON API; they may never have learned about hypermedia as a system at
all.</p>
<p>This is a tragedy, and, frankly, a failure on the part of the thought
leaders in the web development community to properly communicate and
advocate for the hypermedia approach.</p>
<p>Hypermedia was a great idea! It still is!</p>
<p>By the end of this book, you will have the tools and the
<em class="test">language</em> to put this great idea to work in your own
applications. And, further, you will be able to bring the ideas and
concepts of hypermedia systems to the broader web development
community.</p>
<p>Hypermedia can compete, hypermedia <em class="test">can win</em>, hypermedia
<em class="test">has won</em> as an architectural choice against the Single Page
Application approach, but <em class="test">only</em> if smart people (like you) learn
about it, build with it and then tell the world about it.</p>
<blockquote>
<p>Remember the message? “The future is not set. There is no fate but
what we make for ourselves.”</p>
</blockquote><p class="quote-attribution"> Kyle Reese, Terminator 2: Judgement Day</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Hypermedia In Practice</h2>
<p>Clearly, HTML plays a central role in the story we tell here. At the
end of each chapter we will share what we have learned about writing
HTML for hypermedia-driven web applications.</p>
<p>To start, remember that our web applications are not islands. We’re
writing HTML not just for a particular application, but also to play
along with other members of the web. When we write with the hypermedia
<em class="test">system</em> in mind, we’re better able to tap the range of abilities
available to the web.</p>
<p>HTML is hypermedia-friendly when it is written for the full range of
constituents of the hypermedia system. It conveys the state of an
application to people viewing our sites with a browser, as well as to
people listening to screen readers that read sites aloud. It conveys the
aims of our sites to search engines that scrape sites programmatically.
It also conveys its behavior as clearly as possible to other
developers.</p>
<p>No, we can’t fix every problem with good HTML. The mantra that HTML
is “accessible by default” is misleading. We would miss out on important
opportunities if we shunned other technologies like JavaScript. And we
still need to test, a lot, everywhere, to ensure things work as
expected.</p>
<p>But good HTML lets browsers do a <em class="test">lot</em> of work for us.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Hypermedia: A Reintroduction</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_what_is_hypermedia">What Is Hypermedia?</a>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_a_brief_history_of_hypermedia">A Brief History of
Hypermedia</a>
<ul>
<li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_modern_implementation">Modern Implementation</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_the_worlds_most_successful_hypertext_html">The World’s Most
Successful Hypertext: HTML</a>
<ul>
<li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_the_essence_of_html_as_a_hypermedia">The Essence of HTML as a
Hypermedia</a>
<ul>
<li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_anchor_tags">Anchor tags</a>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_form_tags">Form tags</a>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_web_1_0_applications">Web 1.0 applications</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_so_what_isnt_hypermedia">So What Isn’t Hypermedia?</a>
<ul>
<li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_single_page_applications">Single Page Applications</a>
</li></ul>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_why_use_hypermedia">Why Use Hypermedia?</a>
<ul>
<li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_javascript_fatigue">JavaScript Fatigue</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_a_hypermedia_resurgence">A Hypermedia Resurgence?</a>
<ul>
<li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_hypermedia_oriented_javascript_libraries">Hypermedia-Oriented
JavaScript Libraries</a>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_hypermedia_driven_applications">Hypermedia-Driven
Applications</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_when_should_you_use_hypermedia">When Should You Use
Hypermedia?</a>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_when_shouldnt_you_use_hypermedia">When Shouldn’t You Use
Hypermedia?</a>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#_hypermedia_a_sophisticated_modern_system_architecture">Hypermedia:
A Sophisticated, Modern System Architecture</a>
</li><li>
<a href="https://hypermedia.systems/hypermedia-a-reintroduction/#html-note-title">HTML Notes: &lt;div&gt; Soup</a>
</li></ul>
</details>
<div class="division-content">
<p>Hypermedia is a universal technology today, almost as common as
electricity.</p>
<p>Billions of people use hypermedia-based systems every day, mainly by
interacting with the <em class="test">Hypertext Markup Language (HTML)</em> being
exchanged via the <em class="test">Hypertext Transfer Protocol (HTTP)</em> by using a
web browser connected to the World Wide Web.</p>
<p>People use these systems to get their news, check in on friends, buy
things online, play games, send emails and so forth: the variety and
sheer number of online services being delivered by hypermedia is truly
astonishing.</p>
<p>And yet, despite this ubiquity, the topic of hypermedia itself is a
strangely under-explored concept today, left mainly to specialists. Yes,
you can find a lot of tutorials on how to author HTML, create links and
forms, etc. But it is rare to see a discussion of HTML <em class="test">as a
hypermedia</em> and, more broadly, on how an entire hypermedia
<em class="test">system</em> fits together.</p>
<p>This is in contrast with the early web development era when concepts
like <em class="test">Representational State Transfer (REST)</em> and <em class="test">Hypermedia
As The Engine of Application State (HATEOAS)</em> were discussed
frequently, refined and debated among web developers.</p>
<p>In a sad turn of events, today, the world’s most popular hypermedia,
HTML, is often viewed resentfully: it is an awkward, legacy markup
language that must be grudgingly used to build user interfaces in what
are increasingly entirely JavaScript-based web applications.</p>
<p>HTML happens to be there, in the browser, and so we have to use
it.</p>
<p>This is a shame and we hope to convince you that hypermedia is
<em class="test">not</em> simply a piece of legacy technology that we have to accept
and deal with. Instead, we aim to show you that hypermedia is a
tremendously innovative, simple and <em class="test">flexible</em> way to build
robust applications: <em class="test">Hypermedia-Driven Applications</em>.</p>
<p>We hope that by the end of this book you will feel, as we do, that
the hypermedia approach deserves a seat at the table when you, a web
developer, are considering the architecture of your next application.
Creating a Hypermedia-Driven Application on top of a <em class="test">hypermedia
system</em> like the web is a viable and, indeed, often excellent choice
for <em class="test">modern</em> web applications.</p>
<p>(And, as the section on Hyperview will show, not just web
applications.)</p>
<h2 id="_what_is_hypermedia">What Is Hypermedia?</h2>
<blockquote>
<p>Hypertexts: new forms of writing, appearing on computer screens, that
will branch or perform at the reader’s command. A hypertext is a
non-sequential piece of writing; only the computer display makes it
practical.</p>
</blockquote><p class="quote-attribution"> Ted Nelson,
https://archive.org/details/SelectedPapers1977/page/n7/mode/2up</p>
<p>Let us begin at the beginning: what is hypermedia?</p>
<p>Hypermedia is a media, for example a text, that includes
<em class="test">non-linear branching</em> from one location in the media to another,
via, for example, hyperlinks embedded in the media. The prefix “hyper-”
derives from the Greek prefix “ὑπερ-” which means “beyond” or “over”,
indicating that hypermedia <em class="test">goes beyond</em> normal, passively
consumed media like magazines and newspapers.</p>
<p>Hyperlinks are a canonical example of what is called a <em class="test">hypermedia
control</em>:</p>
<dl>
<dt>Hypermedia Control</dt>
<dd>
<p>A hypermedia control is an element in a hypermedia that describes (or
controls) some sort of interaction, often with a remote server, by
encoding information about that interaction directly and completely
within itself.</p>
</dd>
</dl>
<p>Hypermedia controls are what differentiate hypermedia from other
sorts of media.</p>
<p>You may be more familiar with the term <em class="test">hypertext</em>, from whose
Wikipedia page the above quote is taken. Hypertext is a sub-category of
hypermedia and much of this book is going to discuss how to build modern
applications using hypertexts such as HTML, the Hypertext Markup
Language, or HXML, a hypertext used by the Hyperview mobile hypermedia
system.</p>
<p>Hypertexts like HTML function alongside other technologies crucial
for making an entire hypermedia system work: network protocols like
HTTP, other media types such as images and videos, hypermedia servers
(i.e., servers providing hypermedia APIs), sophisticated hypermedia
clients (e.g., web browsers), and so on.</p>
<p>Because of this, we prefer the broader term <em class="test">hypermedia
systems</em> when describing the underlying architecture of applications
built using hypertext, to emphasize the system architecture over the
particular hypermedia being used.</p>
<p>It is the entire hypermedia <em class="test">system architecture</em> that is
underappreciated and ignored by many modern web developers.</p>
<h2 id="_a_brief_history_of_hypermedia">A Brief History of
Hypermedia</h2>
<p>Where did the idea of hypermedia come from?</p>
<p>While there were many precursors to the modern idea of hypertext and
the more general hypermedia, many people point to the 1945 article
<em class="test">As We May Think</em> written by Vannevar Bush in <em class="test">The
Atlantic</em> as a starting point for looking at what has become modern
hypermedia.</p>
<p>In this article Bush described a device called a Memex, which, using
a complex mechanical system of reels and microfilm, along with an
encoding system, would allow users to jump between related frames of
content. The Memex was never actually implemented, but it was an
inspiration for later work on the idea of hypermedia.</p>
<p>The terms “hypertext” and “hypermedia” were coined in 1963 by Ted
Nelson, who would go on to work on the <em class="test">Hypertext Editing System</em>
at Brown University and who later created the <em class="test">File Retrieval and
Editing System (FRESS)</em>, a shockingly advanced hypermedia system for
its time. (This was perhaps the first digital system to have a notion of
“undo”.)</p>
<p>While Nelson was working on his ideas, Douglas Engelbart was busy at
work at the Stanford Research Institute, explicitly attempting to make
Vannevar Bush’s Memex a reality. In 1968, Englebart gave “The Mother of
All Demos” in San Francisco, California.</p>
<p>Englebart demonstrated an unbelievable amount of technology:</p>
<ul>
<li><p>Remote, collaborative text editing with his peers in Menlo
Park</p></li>
<li><p>Video and audio chat</p></li>
<li><p>An integrated windowing system, with window resizing,
etc</p></li>
<li><p>A recognizable hypertext, whereby clicking on underlined text
navigated to new content.</p></li>
</ul>
<p>Despite receiving a standing ovation from a shocked audience after
his talk, it was decades before the technologies Englebart demonstrated
became mainstream.</p>
<h3 id="_modern_implementation">Modern Implementation</h3>
<p>In 1990, Tim Berners-Lee, working at CERN, published the first
website. He had been working on the idea of hypertext for a decade and
had finally, out of desperation at the fact it was so hard for
researchers to share their research, found the right moment and
institutional support to create the World Wide Web:</p>
<blockquote>
<p>Creating the web was really an act of desperation, because the
situation without it was very difficult when I was working at CERN
later. Most of the technology involved in the web, like the hypertext,
like the Internet, multifont text objects, had all been designed
already. I just had to put them together. It was a step of generalising,
going to a higher level of abstraction, thinking about all the
documentation systems out there as being possibly part of a larger
imaginary documentation system.</p>
</blockquote><p class="quote-attribution"> Tim Berners-Lee,
https://britishheritage.org/tim-berners-lee-the-world-wide-web</p>
<p>By 1994 his creation was taking off so quickly that Berners-Lee
founded the W3C, a working group of companies and researchers tasked
with improving the web. All standards created by the W3C were
royalty-free and could be adopted and implemented by anyone, cementing
the open, collaborative nature of the web.</p>
<p>In 2000, Roy Fielding, then at U.C. Irvine, published a seminal PhD
dissertation on the web: “Architectural Styles and the Design of
Network-based Software Architectures.” Fielding had been working on the
open source Apache HTTP Server and his thesis was a description of what
he felt was a <em class="test">new and distinct networking architecture</em> that had
emerged in the early web. Fielding had worked on the initial HTTP
specifications and, in the paper, defined the web’s hypermedia network
model using the term <em class="test">REpresentational State Transfer
(REST)</em>.</p>
<p>Fielding’s work became a major touchstone for early web developers,
giving them a language to discuss the new technical medium they were
building applications in.</p>
<p>We will discuss Fielding’s key ideas in depth in Chapter 2, and try
to correct the record with respect to REST, HATEOAS and hypermedia.</p>
<h2 id="_the_worlds_most_successful_hypertext_html">The World’s Most
Successful Hypertext: HTML</h2>
<blockquote>
<p>In the beginning was the hyperlink, and the hyperlink was with the
web, and the hyperlink was the web. And it was good.</p>
</blockquote><p class="quote-attribution"> Rescuing REST From the API Winter,
https://intercoolerjs.org/2016/01/18/rescuing-rest.html</p>
<p>The system that Berners-Lee, Fielding and many others had created
revolved around a hypermedia: HTML. HTML started as a read-only
hypermedia, used to publish (at first) academic documents. These
documents were linked together via anchor tags which created
<em class="test">hyperlinks</em> between them, allowing users to quickly navigate
between documents.</p>
<p>When HTML 2.0 was released, it introduced the notion of the
<code>form</code> tag, joining the anchor tag (i.e., hyperlink) as a
second hypermedia control. The introduction of the form tag made
building <em class="test">applications</em> on the web viable by providing a
mechanism for <em class="test">updating</em> resources, rather than just reading
them.</p>
<p>It was at this point that the web transitioned from an interesting
document-oriented system to a compelling <em class="test">application
architecture</em>.</p>
<p>Today HTML is the most widely used hypermedia in existence and this
book naturally assumes that the reader has a reasonable familiarity with
it. You do not need to be an HTML (or CSS) expert to understand the code
in this book, but the better you understand the core tags and concepts
of HTML, the more you will get out of it.</p>
<h3 id="_the_essence_of_html_as_a_hypermedia">The Essence of HTML as a
Hypermedia</h3>
<p>Let us consider these two defining hypermedia elements (that is the
two defining <em class="test">hypermedia controls</em>) of HTML, the anchor tag and
the form tag, in a bit of detail.</p>
<h4 id="_anchor_tags">Anchor tags</h4>
<p>Anchor tags are so familiar as to be boring but, as the original
hypermedia control, it is worth reviewing the mechanics of hyperlinks to
get our minds in the right place for developing a deeper understanding
of hypermedia.</p>
<p>Consider a simple anchor tag, embedded within a larger HTML
document:</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"https://hypermedia.systems/"</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a> Hypermedia Systems</span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A simple hyperlink</p></figcaption>
</figure>
<p>An anchor tag consists of the tag itself,
<code>&lt;a&gt;&lt;/a&gt;</code>, as well as the attributes and content
within the tag. Of particular interest is the <code>href</code>
attribute, which specifies a <em class="test">hypertext reference</em> to another
document or document fragment. It is this attribute that makes the
anchor tag a hypermedia control.</p>
<p>In a typical web browser, this anchor tag would be interpreted to
mean:</p>
<ul>
<li><p>Show the text “Hypermedia Systems” in a manner indicating that it
is clickable</p></li>
<li><p>When the user clicks on that text, issue an HTTP <code>GET</code>
request to the URL <code>https://hypermedia.systems/</code></p></li>
<li><p>Take the HTML content in the body of the HTTP response to this
request and replace the entire screen in the browser as a new document,
updating the navigation bar to this new URL.</p></li>
</ul>
<p>Anchors provide the main mechanism we use to navigate around the web
today, by selecting links to navigate from document to document, or from
resource to resource. <a class="ref" href="#fig-get-in-action">[fig-get-in-action]</a> shows what a user interaction with
an anchor tag/hyperlink looks like in visual form.</p>
<figure id="fig-get-in-action">
<div>
<div data-align="start">
<pre><code>┌────────────────────────┐ ┌─HTTP REQUEST────────────────┐
│ BROWSER X │ │ │
├────────────────────────┤ │ GET / │
│ │ │ Host: hypermedia.systems │
│ lorem ipsum dolor │ └─────────────────────────────┘
│ │
│ Hypermedia Systems ────┼───────────┐
│ ────────────────── │ │
│ sit amet │ │
│ │ │
└────────────────────────┘ │
┌──────▼──────┐
│ H T T P │
│ S E R V E R │
└──────┬──────┘
┌────────────────────────┐ │
│ BROWSER X │ │
├────────────────────────┤ │
│ │ │
│ HYPERMEDIA SYSTEMS ◀───────────┘
│ │
│ The revolutionary │ ┌─HTTP RESPONSE───────────────┐
│ │ │ │
│ ideas that empowered...│ │ 200 OK │
│ │ │ ... │
└────────────────────────┘ │ &lt;h1&gt;Hypermedia Systems&lt;/h1&gt; │
│ ... │
└─────────────────────────────┘
</code></pre>
</div>
</div>
<figcaption><p>An HTTP GET In Action</p></figcaption>
</figure>
<p>When the link is clicked the browser (or, as we sometimes refer to
it, the <em class="test">hypermedia client</em>) initiates an HTTP <code>GET</code>
request to the URL encoded in the link’s <code>href</code>
attribute.</p>
<p>Note that the HTTP request includes additional data (i.e.,
<em class="test">metadata</em>) on what, exactly, the browser wants from the server,
in the form of headers. We will discuss these headers, and HTTP in more
depth in Chapter 2.</p>
<p>The <em class="test">hypermedia server</em> then responds to this request with a
<em class="test">hypermedia response</em> — the HTML — for the new page. This may
seem like a small and obvious point, but it is an absolutely crucial
aspect of a truly RESTful <em class="test">hypermedia system</em>: the client and
server must communicate via hypermedia!</p>
<h4 id="_form_tags">Form tags</h4>
<p>Anchor tags provide <em class="test">navigation</em> between documents or
resources, but don’t allow you to update those resources. That
functionality falls to the form tag.</p>
<p>Here is a simple example of a form in HTML:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/signup"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"post"</span><span class="dt">&gt;</span></span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Enter Email To Sign Up"</span><span class="dt">&gt;</span></span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="dt">&gt;</span>Sign Up<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A simple form</p></figcaption>
</figure>
<p>Like an anchor tag, a form tag consists of the tag itself,
<code>&lt;form&gt;&lt;/form&gt;</code>, combined with the attributes and
content within the tag. Note that the form tag does not have an
<code>href</code> attribute, but rather has an <code>action</code>
attribute that specifies where to issue an HTTP request.</p>
<p>Furthermore, it also has a <code>method</code> attribute, which
specifies exactly which HTTP “method” to use. In this example the form
is asking the browser to issue a <code>POST</code> request.</p>
<p>In contrast with anchor tags, the content and tags <em class="test">within</em> a
form can have an effect on the hypermedia interaction that the form
makes with a server. The <em class="test">values</em> of <code>input</code> tags and
other tags such as <code>select</code> tags will be included with the
HTTP request when the form is submitted, as URL parameters in the case
of a <code>GET</code> and as part of the request body in the case of a
<code>POST</code>. This allows a form to include an arbitrary amount of
information collected from a user in a request, unlike the anchor
tag.</p>
<p>In a typical browser this form tag and its contents would be
interpreted by the browser roughly as follows:</p>
<ul>
<li><p>Show a text input and a “Sign Up” button to the user</p></li>
<li><p>When the user submits the form by clicking the “Sign Up” button
or by hitting the enter key while the input element is focused, issue an
HTTP <code>POST</code> request to the path <code>/signup</code> on the
“current” server</p></li>
<li><p>Take the HTML content in the body of the HTTP response body and
replace the entire screen in the browser as a new document, updating the
navigation bar to this new URL.</p></li>
</ul>
<p>This mechanism allows the user to issue requests to <em class="test">update the
state</em> of resources on the server. Note that despite this new type
of request the communication between client and server is still done
entirely with <em class="test">hypermedia</em>.</p>
<p>It is the form tag that makes Hypermedia-Driven Applications
possible.</p>
<p>If you are an experienced web developer you probably recognize that
we are omitting a few details and complications here. For example, the
response to a form submission often <em class="test">redirects</em> the client to a
different URL.</p>
<p>This is true, and we will get down into the muck with forms in more
detail in later chapters but, for now, this simple example suffices to
demonstrate the core mechanism for updating system state purely within
hypermedia. <a class="ref" href="#fig-post-in-action">[fig-post-in-action]</a> is a diagram of the
interaction.</p>
<figure id="fig-post-in-action">
<div>
<div data-align="start">
<pre><code>┌────────────────────────┐ ┌─HTTP REQUEST────────────────┐
│ BROWSER X │ │ │
├────────────────────────┤ │ POST / │
│ │ │ Host: hypermedia.systems │
│ SIGN UP │ │ ... │
│ ┌────────────────────┐ │ │ email=joe@example.com │
│ │ joe@example.com │ │ └─────────────────────────────┘
│ └────────────────────┘ │
│ ┌─────────┐ ────┼───────────┐
│ │ Sign up │ │ │
│ └─────────┘ │ │
└────────────────────────┘ │
┌──────▼──────┐
│ H T T P │
│ S E R V E R │
└──────┬──────┘
┌────────────────────────┐ │
│ BROWSER X │ │
├────────────────────────┤ │
│ │ │
│ THANK YOU FOR SIGNING ◀───────────┘
│ UP │
│ │ ┌─HTTP RESPONSE───────────────┐
│ │ │ │
│ │ │ 201 Created │
│ │ │ ... │
└────────────────────────┘ │ &lt;h1&gt;Thank you for signing │
│ up&lt;/h1&gt; │
└─────────────────────────────┘
</code></pre>
</div>
</div>
<figcaption><p>An HTTP POST In Action</p></figcaption>
</figure>
<h4 id="_web_1_0_applications">Web 1.0 applications</h4>
<p>As someone interested in web development, the above diagrams and
discussion are probably very familiar to you. You may even find this
content boring. But take a step back and consider the fact that these
two hypermedia controls, anchors and forms, are the <em class="test">only</em> native
ways for a user to interact with a server in plain HTML.</p>
<p>Only two tags!</p>
<p>And yet, armed with only these two tags, the early web was able to
grow exponentially and offer a staggeringly large amount of online,
dynamic functionality to billions of people.</p>
<p>This is strong evidence of the power of hypermedia. Even today, in a
web development world increasingly dominated by large JavaScript-centric
front end frameworks, many people choose to use simple vanilla HTML to
achieve their application goals and are often perfectly happy with the
results.</p>
<p>These two tags give a tremendous amount of expressive power to
HTML.</p>
<h3 id="_so_what_isnt_hypermedia">So What Isn’t Hypermedia?</h3>
<p>So links and forms are the two main hypermedia-based mechanisms for
interacting with a server available in HTML.</p>
<p>Now let’s consider a different approach: let’s interact with a server
by issuing an HTTP request via JavaScript. To do this, we will use the
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"><code>fetch()</code></a>
API, a popular API for issuing an “Asynchronous JavaScript and XML,” or
AJAX request, available in all modern web browsers:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> onclick</span><span class="op">=</span><span class="st">"fetch('/api/v1/contacts/1') </span><span class="er">&lt;</span><span class="st">1&gt;</span></span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a><span class="st"> .then(response =&gt; response.json()) </span><span class="er">&lt;</span><span class="st">2&gt;</span></span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a><span class="st"> .then(data =&gt; updateUI(data)) "</span><span class="dt">&gt;</span> <span class="er">&lt;</span>3&gt;</span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> Fetch Contact</span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>JavaScript</p></figcaption>
</figure>
<ol>
<li><p>Issue the request.</p></li>
<li><p>Convert the response to a JavaScript object.</p></li>
<li><p>Invoke the <code>updateUI()</code> function with the
object.</p></li>
</ol>
<p>This button has an <code>onclick</code> attribute that specifies some
JavaScript to run when the button is clicked.</p>
<p>The JavaScript will issue an AJAX HTTP <code>GET</code> request to
<code>/api/v1/contacts/1</code> using <code>fetch()</code>. An AJAX
request is like a “normal” HTTP request, but it is issued “behind the
scenes” by the browser. The user does not see a request indicator from
the browser as they would with normal links and forms. Additionally,
unlike requests issued by those hypermedia controls, it is up to the
JavaScript code to handle the response from the server.</p>
<p>Despite AJAX having XML as part of its acronym, today the HTTP
response to this request would almost certainly be in the JavaScript
Object Notation (JSON) format rather than XML.</p>
<p>An HTTP response to this request might look something like this:</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="fu">{</span> <span class="er">&lt;1&gt;</span></span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a> <span class="dt">"id"</span><span class="fu">:</span> <span class="dv">42</span><span class="fu">,</span> <span class="er">&lt;2&gt;</span></span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a> <span class="dt">"email"</span> <span class="fu">:</span> <span class="st">"json-example@example.org"</span> <span class="er">&lt;</span><span class="dv">3</span><span class="er">&gt;</span></span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<figcaption><p>JSON</p></figcaption>
</figure>
<ol>
<li><p>The start of a JSON object.</p></li>
<li><p>A property, in this case with the name <code>id</code> and the
value <code>42</code>.</p></li>
<li><p>Another property, the email of the contact with this id.</p></li>
</ol>
<p>The JavaScript code above converts the JSON text received from the
server into a JavaScript object by calling the <code>json()</code>
method on it. This new JavaScript object is then handed off to the
<code>updateUI()</code> method.</p>
<p>The <code>updateUI()</code> method is responsible for updating the UI
based on the data encoded in the JavaScript Object, perhaps by
displaying the contact in a bit of HTML generated via a client-side
template in the JavaScript application.</p>
<p>The details of exactly what the <code>updateUI()</code> function does
aren’t important for our discussion.</p>
<p>What <em class="test">is</em> important, what is the <em class="test">crucial</em> aspect of
this JSON-based server interaction is that it is <em class="test">not</em> using
hypermedia. The JSON API being used here does not return a hypermedia
response. There are no <em class="test">hyperlinks</em> or other hypermedia-style
controls in it.</p>
<p>This JSON API is, rather, a <em class="test">Data API</em>.</p>
<p>Because the response is in JSON and is <em class="test">not</em> hypermedia, the
JavaScript <code>updateUI()</code> method must understand how to turn
this contact data into HTML.</p>
<p>In particular, the code in <code>updateUI()</code> needs to know
about the <em class="test">internal structure</em> and meaning of the data.</p>
<p>It needs to know:</p>
<ul>
<li><p>Exactly how the fields in the JSON data object are structured and
named.</p></li>
<li><p>How they relate to one another.</p></li>
<li><p>How to update the local data this new data corresponds
with.</p></li>
<li><p>How to render this data to the browser.</p></li>
<li><p>What additional actions/API end points can be called with this
data.</p></li>
</ul>
<p>In short, the logic in <code>updateUI()</code> needs to have intimate
knowledge of the API endpoint at <code>/api/v1/contact/1</code>,
knowledge provided via some side-channel beyond the response itself. As
a result, the <code>updateUI()</code> code and the API have a strong
relationship, known as <em class="test">tight coupling</em>: if the format of the
JSON response changes, then the code for <code>updateUI()</code> will
almost certainly also need to be changed as well.</p>
<h4 id="_single_page_applications">Single Page Applications</h4>
<p>This bit of JavaScript, while very modest, is the organic beginnings
of a much larger conceptual approach to building web applications. This
is the beginning of a <em class="test">Single Page Application (SPA)</em>. The web
application is no longer navigating <em class="test">between</em> pages using
hypermedia controls as was the case with links and forms.</p>
<p>Instead, the application is exchanging <em class="test">plain data</em> with the
server and then updating the content <em class="test">within</em> a single page.</p>
<p>When this strategy or architecture is adopted for an entire
application, everything happens on a “Single Page” and, thus the
application becomes a “Single Page Application.”</p>
<p>The Single Page Application architecture is extremely popular today
and has been the dominant approach to building web applications for the
last decade. This can be observed by the high level of mind-share and
discussion it has received in the industry.</p>
<p>Today the vast majority of Single Page Applications adopt far more
sophisticated frameworks for managing their user interface than this
simple example shows. Popular libraries such as React, Angular, Vue.js,
etc. are now the common — indeed, the standard — way to build web
applications.</p>
<p>With these more complex frameworks developers typically work with an
elaborate client-side model — that is, with JavaScript objects stored
locally in the browser’s memory that represent the “model” or “domain”
of your application. These JavaScript objects are updated via JavaScript
code and the framework then “reacts” to these changes, updating the user
interface.</p>
<p>When the user interface is updated by a user these changes also flow
<em class="test">into</em> the model objects, establishing a “two-way” binding
mechanism: the model can update the UI, and the UI can update the
model.</p>
<p>This is a much more sophisticated approach to a web client than
hypermedia, and it typically does away almost entirely with the
underlying hypermedia infrastructure available in the browser.</p>
<p>HTML is still used to build user interfaces, but the
<em class="test">hypermedia</em> aspect of the two major hypermedia controls, anchors
and forms, are unused. Neither tag interacts with a server via their
native <em class="test">hypermedia</em> mechanism. Rather, they become user interface
elements that drive local interactions with the in-memory domain model
via JavaScript, which is then synchronized with the server using plain
data JSON APIs.</p>
<p>So, as with our simple button above, the Single Page Application
approach foregoes the hypermedia architecture. It leaves aside the
advantages of the existing RESTful architecture of the web and the
built-in functionality found in HTML’s native hypermedia controls in
favor of JavaScript driven behaviors.</p>
<p>SPAs are much more like <em class="test">thick client applications</em>, that is,
like the client-server applications of the 1980s — an architecture
popular <em class="test">before</em> the web came along and that the web was, in many
ways, a reaction to.</p>
<p>This approach isn’t necessarily wrong, of course: there are times
when a thick client approach is the appropriate choice for an
application. But it is worth thinking about <em class="test">why</em> web developers
so frequently make this choice without considering other alternatives,
and if there are reasons <em class="test">not</em> to go down this path.</p>
<h2 id="_why_use_hypermedia">Why Use Hypermedia?</h2>
<blockquote>
<p>The emerging norm for web development is to build a React single-page
application, with server rendering. The two key elements of this
architecture are something like:</p>
<ol>
<li><p>The main UI is built &amp; updated in JavaScript using React or
something similar.</p></li>
<li><p>The backend is an API that that application makes requests
against.</p></li>
</ol>
<p>This idea has really swept the internet. It started with a few major
popular websites and has crept into corners like marketing sites and
blogs.</p>
</blockquote><p class="quote-attribution"> Tom MacWright, <a href="https://macwright.com/2020/05/10/spa-fatigue.html">https://macwright.com/2020/05/10/spa-fatigue.html</a></p>
<p>The JavaScript-based Single Page Application approach has taken the
web development world by storm, and if there was one single reason for
its wild success it was this: The Single Page Application offers a far
more interactive and immersive experience than the old, gronky, Web 1.0
hypermedia-based applications could. SPAs had the ability to smoothly
update elements inline on a page without a dramatic reload of the entire
document, they had the ability to use CSS transitions to create nice
visual effects, and the ability to hook into arbitrary events like mouse
movements.</p>
<p>All of these abilities give JavaScript-based applications a huge
advantage in building sophisticated user experiences.</p>
<p>Given the popularity, power and success of this modern approach to
building web applications, why on earth would you consider an older,
clunkier and less popular approach like hypermedia?</p>
<h3 id="_javascript_fatigue">JavaScript Fatigue</h3>
<p>We are glad you asked!</p>
<p>It turns out that the hypermedia architecture, even in its original
Web 1.0 form, has a number of advantages when compared with the Single
Page Application + JSON Data API approach. Three of the biggest are:</p>
<ul>
<li><p>It is an extremely <em class="test">simple</em> approach to building web
applications.</p></li>
<li><p>It is extremely tolerant of content and API changes. In fact, it
thrives on them!</p></li>
<li><p>It leverages tried and true features of web browsers, such as
caching.</p></li>
</ul>
<p>The first two advantages, in particular, address major pain points in
modern web development:</p>
<ul>
<li><p>Single Page Application infrastructure has become extremely
complex, often requiring an entire team to manage.</p></li>
<li><p>JSON API churn — constant changes made to JSON APIs to support
application needs — has become a major pain point for many application
teams.</p></li>
</ul>
<p>The combination of these two problems, along with other issues such
as JavaScript library churn, has led to a phenomenon known as
“JavaScript Fatigue.” This refers to a general sense of exhaustion with
all the hoops that are necessary to jump through to get anything done in
modern-day web applications.</p>
<p>We believe that a hypermedia architecture can help cure JavaScript
Fatigue for many developers and teams.</p>
<p>But if hypermedia is so great, and if it addresses so many of the
problems that beset the web development industry, why was it set aside
in the first place? After all, hypermedia was there first. Why didn’t
web developers just stick with it?</p>
<p>There are two major reasons hypermedia hasn’t made a comeback in web
development.</p>
<p>The first is this: the expressiveness of HTML <em class="test">as a
hypermedia</em> hasn’t changed much, if at all, since HTML 2.0, which
was released <em class="test">in the mid 1990s</em>. Many new <em class="test">features</em> have
been added to HTML, of course, but there haven’t been <em class="test">any</em> major
new ways to interact with a server in HTML in almost three decades.</p>
<p>HTML developers still only have anchor tags and forms available as
hypermedia controls, and those hypermedia controls can still only issue
<code>GET</code> and <code>POST</code> requests.</p>
<p>This baffling lack of progress by HTML leads immediately to the
second, and perhaps more practical reason that HTML-as-hypermedia has
fallen on hard times: as the interactivity and expressiveness of HTML
has remained frozen, the demands of web users have continued to
increase, calling for more and more interactive web applications.</p>
<p>JavaScript-based applications coupled to data-oriented JSON APIs have
stepped in as a way to provide these more sophisticated user interfaces.
It was the <em class="test">user experience</em> that you could achieve in
JavaScript, and that you couldn’t achieve in plain HTML, that drove the
web development community to the JavaScript-based Single Page
Application approach. The shift was not driven by any inherent
superiority of the Single Page Application as a system architecture.</p>
<p>It didn’t have to be this way. There is nothing <em class="test">intrinsic</em> to
the idea of hypermedia that prevents it from having a richer, more
expressive interactivity model than vanilla HTML. Rather than moving
away from a hypermedia-based approach, the industry could have demanded
more interactivity from HTML.</p>
<p>Instead, building thick-client style applications within web browsers
became the standard, in an understandable move to a more familiar model
for building rich applications.</p>
<p>Not everyone set aside hypermedia, of course. There have been heroic
efforts to continue to advance hypermedia outside of HTML, efforts like
HyTime, VoiceXML, and HAL.</p>
<p>But HTML, the most widely used hypermedia in the world, stopped
making progress as a hypermedia. The web development world moved on,
solving the interactivity problems with HTML by adopting
JavaScript-based SPAs and, mostly inadvertently, a completely different
system architecture.</p>
<h2 id="_a_hypermedia_resurgence">A Hypermedia Resurgence?</h2>
<p>It is interesting to think about how HTML <em class="test">could</em> have
advanced. Instead of stalling as a hypermedia, how could HTML have
continued to develop? Could it have kept adding new hypermedia controls
and increasing the expressiveness of existing ones? Would it have been
possible to build modern web applications within this original,
hypermedia-oriented and RESTful model that made the early web so
powerful, so flexible, so much fun?</p>
<p>This might seem like idle speculation, but we have some good news on
this score: in the last decade a few idiosyncratic, alternative front
end libraries have arisen that attempt to get HTML moving again.
Ironically, these libraries are written in JavaScript, the technology
that supplanted HTML as the center of web development.</p>
<p>However, these libraries use JavaScript not as a <em class="test">replacement</em>
for the fundamental hypermedia system of the web.</p>
<p>Instead, they use JavaScript to augment HTML itself <em class="test">as a
hypermedia</em>.</p>
<p>These <em class="test">hypermedia-oriented</em> libraries re-center hypermedia as
the core technology in web applications.</p>
<h3 id="_hypermedia_oriented_javascript_libraries">Hypermedia-Oriented
JavaScript Libraries</h3>
<p>In the web development world there is an ongoing debate between the
Single Page Application (SPA) approach and what is now being called the
“Multi-Page Application” (MPA) approach. MPA is a modern name for the
old, Web 1.0 way of building web applications, using links and forms
located on multiple web pages, submitting HTTP requests and getting HTML
responses.</p>
<p>MPA applications, by their nature, are Hypermedia-Driven
Applications: after all, they are exactly what Roy Fielding was
describing in his dissertation.</p>
<p>These applications tend to be clunky, but they work reasonably well.
Many web developers and teams choose to accept the limitations of plain
HTML in the interest of simplicity and reliability.</p>
<p>Rich Harris, creator of Svelte.js, a popular SPA library, and a
thought-leader on the SPA side of the debate, has proposed a mix of this
older MPA style and the newer SPA style. Harris calls this approach to
building web applications “transitional,” in that it attempts to blend
the MPA approach and the newer SPA approach into a coherent whole. (This
is somewhat similar to the “transitional” trend in architecture, which
combines traditional and modern architectural styles.)</p>
<p>“Transitional” is a fitting term for mixed-style applications, and it
offers a reasonable compromise between the two approaches, using either
one as appropriate on a case-by-case basis.</p>
<p>But this compromise still feels unsatisfactory.</p>
<p>Must we default to having these two very different architectural
models in our applications?</p>
<p>Recall that the crux of the trade-off between SPAs and MPAs is the
<em class="test">user experience</em>, or interactivity of the application. This
typically drives the decision to choose one approach versus the other
for an application or — in the case of a “transitional” application —
for a particular feature.</p>
<p>It turns out that by adopting a hypermedia-oriented library, the
interactivity gap between the MPA and the SPA approach closes
dramatically. You can use the MPA approach, that is, the hypermedia
approach, for much more of your application without compromising your
user interface. You might even be able to use the hypermedia approach
for <em class="test">all</em> your application needs.</p>
<p>Rather than having an SPA with a bit of hypermedia around the edges,
or some mix of the two approaches, you can often create a web
application that is <em class="test">primarily</em> or <em class="test">entirely</em>
hypermedia-driven, and that still satisfies the interactivity that your
users require.</p>
<p>This can <em class="test">tremendously</em> simplify your web application and
produce a much more coherent and understandable piece of software. While
there are still times and places for the more complex SPA approach,
which we will discuss later in the book, by adopting a hypermedia-first
approach and using a hypermedia-oriented library to push HTML as far as
possible, your web application can be powerful, interactive <em class="test">and</em>
simple.</p>
<p>One such hypermedia oriented library is <a href="https://htmx.org">htmx</a>. Htmx will be the focus of Part Two of
this book. We show that you can, in fact, create many common “modern” UI
features found in sophisticated Single Page Applications by instead
using the hypermedia model.</p>
<p>And, it is refreshingly fun and simple to do so.</p>
<h3 id="_hypermedia_driven_applications">Hypermedia-Driven
Applications</h3>
<p>When building a web application with htmx the term Multi-Page
Application applies <em class="test">roughly</em>, but it doesn’t fully characterize
the core of the application architecture. As you will see, htmx doesn’t
<em class="test">need</em> to replace entire pages, and, in fact, an htmx-based
application can reside entirely within a single page. We don’t recommend
this practice, but it is possible!</p>
<p>So it isn’t quite right to call web applications built with htmx
“Multi-Page Applications.” What the older Web 1.0 MPA approach and the
newer hypermedia-oriented library powered applications have in common is
their use of <em class="test">hypermedia</em> as their core technology and
architecture.</p>
<p>Therefore, we use the term <em class="test">Hypermedia-Driven Applications
(HDAs)</em> to describe both.</p>
<p>This clarifies that the core distinction between these two approaches
and the SPA approach <em class="test">isn’t</em> the number of pages in the
application, but rather the underlying <em class="test">system</em> architecture.</p>
<dl>
<dt>Hypermedia-Driven Application (HDA)</dt>
<dd>
<p>A web application that uses <em class="test">hypermedia</em> and <em class="test">hypermedia
exchanges</em> as its primary mechanism for communicating with a
server.</p>
</dd>
</dl>
<p>So, what does an HDA look like up close?</p>
<p>Let’s look at an htmx-powered implementation of the simple
JavaScript-powered button above:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/1"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#contact-ui"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a> Fetch Contact</span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>An htmx implementation</p></figcaption>
</figure>
<ol>
<li><p>issues a <code>GET</code> request to <code>/contacts/1</code>,
replacing the <code>contact-ui</code>.</p></li>
</ol>
<p>As with the JavaScript powered button, this button has been annotated
with some attributes. However, in this case we do not have any
(explicit) JavaScript scripting.</p>
<p>Instead, we have <em class="test">declarative</em> attributes much like the
<code>href</code> attribute on anchor tags and the <code>action</code>
attribute on form tags. The <code>hx-get</code> attribute tells htmx:
“When the user clicks this button, issue a <code>GET</code> request to
<code>/contacts/1</code>.” The <code>hx-target</code> attribute tells
htmx: “When the response returns, take the resulting HTML and place it
into the element with the id <code>contact-ui</code>.”</p>
<p>Here we get to the crux of htmx and how it allows you to build
Hypermedia-Driven Applications:</p>
<p><em class="test">The HTTP response from the server is expected to be in HTML
format, not JSON</em>.</p>
<p>An HTTP response to this htmx-driven request might look something
like this:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">details</span><span class="dt">&gt;</span></span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a> Contact: HTML Example</span>
<span id="cb8-4"><a aria-hidden="true" href="#cb8-4" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-5"><a aria-hidden="true" href="#cb8-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-6"><a aria-hidden="true" href="#cb8-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:html-example@example.com"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb8-7"><a aria-hidden="true" href="#cb8-7" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-8"><a aria-hidden="true" href="#cb8-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">details</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>HTML</p></figcaption>
</figure>
<p>This small bit of HTML would be placed into the element in the DOM
with the id <code>contact-ui</code>.</p>
<p>Thus, this htmx-powered button is exchanging <em class="test">hypermedia</em> with
the server, just like an anchor tag or form might, and thus the
interaction is still using the basic hypermedia model of the web. Htmx
<em class="test">is</em> adding functionality to this button (via JavaScript), but
that functionality is <em class="test">augmenting</em> HTML as a hypermedia. Htmx
extends the hypermedia system of the web, rather than <em class="test">replacing</em>
that hypermedia system with a totally different architecture.</p>
<p>Despite looking superficially similar to one another it turns out
that this htmx-powered button and the JavaScript-based button are using
extremely different system architectures and, thus, approaches to web
development.</p>
<p>As we walk through building a Hypermedia-Driven Application in this
book, the differences between the two approaches will become more and
more apparent.</p>
<h2 id="_when_should_you_use_hypermedia">When Should You Use
Hypermedia?</h2>
<p>Hypermedia is often, though <em class="test">not always</em>, a great choice for a
web application.</p>
<p>Perhaps you are building a website or application that simply doesn’t
<em class="test">need</em> a huge amount of user-interactivity. There are many useful
web applications like this, and there is no shame in it! Applications
like Amazon, eBay, any number of news sites, shopping sites, message
boards and so on don’t need a massive amount of interactivity to be
effective: they are mainly text and images, which is exactly what the
web was designed for.</p>
<p>Perhaps your application adds most of its value on the <em class="test">server
side</em>, by coordinating users or by applying sophisticated data
analysis and then presenting it to a user. Perhaps your application adds
value by simply sitting in front of a well-designed database, with
simple Create-Read-Update-Delete (CRUD) operations. Again, there is no
shame in this!</p>
<p>In any of these cases, using a hypermedia approach would likely be a
great choice: the interactivity needs of these applications are not
dramatic, and much of the value of these applications lives on the
server side, rather than on the client side.</p>
<p>All of these applications are amenable to what Roy Fielding called
“large-grain hypermedia data transfers”: you can simply use anchor tags
and forms, with responses that return entire HTML documents from
requests, and things will work just fine. This is exactly what the web
was designed to do!</p>
<p>By adopting the hypermedia approach for these applications, you will
save yourself a huge amount of client-side complexity that comes with
adopting the Single Page Application approach: there is no need for
client-side routing, for managing a client-side model, for hand-wiring
in JavaScript logic, and so forth. The back button will “just work.”
Deep linking will “just work.” You will be able to focus your efforts on
your server, where your application is actually adding value.</p>
<p>And, by layering htmx or another hypermedia-oriented library on top
of this approach, you can address many of the usability issues that come
with vanilla HTML and take advantage of finer-grained hypermedia
transfers. This opens up a whole slew of new user interface and
experience possibilities, making the set of applications that can be
built using hypermedia <em class="test">much</em> larger.</p>
<p>But more on that later.</p>
<h2 id="_when_shouldnt_you_use_hypermedia">When Shouldn’t You Use
Hypermedia?</h2>
<p>So, what about that <em class="test">not always</em>? When isn’t hypermedia going
to work well for an application?</p>
<p>One example that springs immediately to mind is an online spreadsheet
application. In the case of a spreadsheet, updating one cell could have
a large number of cascading changes that need to be made across the
entire sheet. Worse, this might need to happen <em class="test">on every
keystroke</em>.</p>
<p>In this case we have a highly dynamic user interface without clear
boundaries as to what might need to be updated given a particular
change. Introducing a hypermedia-style server round-trip on every cell
change would hurt performance tremendously.</p>
<p>This is simply not a situation amenable to the “large-grain
hypermedia data transfer” approach of the web. For an application like
this we would certainly recommend looking into using a sophisticated
client-side JavaScript approach.</p>
<p><em class="test">However</em> even in the case of an online spreadsheet there are
likely areas where the hypermedia approach might help.</p>
<p>The spreadsheet application likely also has a settings page. And
perhaps that settings page <em class="test">is</em> amenable to the hypermedia
approach. If it is simply a set of relatively straight-forward forms
that need to be persisted to the server, the chances are good that
hypermedia would, in fact, work great for this part of the app.</p>
<p>And, by adopting hypermedia for that part of your application, you
might be able to simplify that part of the application quite a bit. You
could then save more of your application’s <em class="test">complexity budget</em>
for the core, complicated spreadsheet logic, keeping the simple stuff
simple.</p>
<p>Why waste all the complexity associated with a heavy JavaScript
framework on something as simple as a settings page?</p>
<div id="sidebar">
<div>
<div>
<p><strong>A Complexity Budget</strong></p>
</div>
<div>
<p>Any software project has a complexity budget, explicit or not: there
is only so much complexity a given development team can tolerate and
every new feature and implementation choice adds at least a bit more to
the overall complexity of the system.</p>
<p>What is particularly nasty about complexity is that it tends to grow
exponentially: one day you can keep the entire system in your head and
understand the ramifications of a particular change, and a week later
the whole system seems intractable. Even worse, efforts to help control
complexity, such as introducing abstractions or infrastructure to manage
the complexity, often end up making things even more complex. Truly, the
job of the good software engineer is to keep complexity under
control.</p>
<p>The sure-fire way to keep complexity down is also the hardest: say
no. Pushing back on feature requests is an art and, if you can learn to
do it well, making people feel like <em class="test">they</em> said no, you will go
far.</p>
<p>Sadly this is not always possible: some features will need to be
built. At this point the question becomes: “what is the simplest thing
that could possibly work?” Understanding the possibilities available in
the hypermedia approach will give you another tool in your “simplest
thing” tool chest.</p>
</div>
</div>
</div>
<h2 id="_hypermedia_a_sophisticated_modern_system_architecture">Hypermedia:
A Sophisticated, Modern System Architecture</h2>
<p>Hypermedia is often regarded as an old and antiquated technology in
web development circles, useful perhaps for static websites but
certainly not a realistic choice for modern, sophisticated web
applications.</p>
<p>Seriously? Are we claiming that modern web applications can be built
using it?</p>
<p>Yes, seriously.</p>
<p>Contrary to current popular opinion, hypermedia is an
<em class="test">innovative</em> and <em class="test">modern</em> system architecture for building
applications, in some ways <em class="test">more modern</em> than the prevailing
Single Page Application approaches. In the remainder of this book we
will reintroduce you to the core, practical concepts of hypermedia and
then demonstrate exactly how you can take advantage of this system
architecture in your own software.</p>
<p>In the coming chapters you will develop a firm understanding of all
the benefits and techniques enabled by this approach. We hope that, in
addition, you will also become as passionate about it as we are. <span id="_html_notes_div_soup"></span></p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: &lt;div&gt; Soup</h2>
<p>The best-known kind of messy HTML is <code>&lt;div&gt;</code>
soup.</p>
<p>When developers fall back on the generic <code>&lt;div&gt;</code> and
<code>&lt;span&gt;</code> elements instead of more meaningful tags, we
either degrade the quality of our websites or create more work for
ourselves — probably both.</p>
<p>For example, instead of adding a button using the dedicated
<code>&lt;button&gt;</code> element, a <code>&lt;div&gt;</code> element
might have a <code>click</code> event listener added to it.</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"bg-accent padding-4 rounded-2"</span><span class="ot"> onclick</span><span class="op">=</span><span class="st">"doStuff()"</span><span class="dt">&gt;</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> Do stuff</span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>There are two main issues with this button:</p>
<ul>
<li><p>It’s not focusable — the Tab key won’t get you to it.</p></li>
<li><p>There’s no way for assistive tools to tell that it’s a
button.</p></li>
</ul>
<p>Yes, we can fix that by adding <code>role="button"</code> and
<code>tabindex="0"</code>:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"bg-accent padding-4 rounded-2"</span></span>
<span id="cb10-2"><a aria-hidden="true" href="#cb10-2" tabindex="-1"></a><span class="ot"> role</span><span class="op">=</span><span class="st">"button"</span></span>
<span id="cb10-3"><a aria-hidden="true" href="#cb10-3" tabindex="-1"></a><span class="ot"> tabindex</span><span class="op">=</span><span class="st">"0"</span></span>
<span id="cb10-4"><a aria-hidden="true" href="#cb10-4" tabindex="-1"></a><span class="ot"> onclick</span><span class="op">=</span><span class="st">"doStuff()"</span><span class="dt">&gt;</span>Do stuff<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>These are easy fixes, but they’re things you have to
<em class="test">remember</em>. It’s also not obvious from the HTML source that this
is a button, making the source harder to read and the absence of these
attributes harder to spot. The source code of pages with div soup is
difficult to edit and debug.</p>
<p>To avoid div soup, become friendly with the HTML spec of available
tags, and consider each tag another tool in your tool chest. There might
be things there you don’t remember from before! (With the 113 elements
currently defined in the spec, it’s more of a tool <em class="test">shed</em>).</p>
<p>Of course, not every UI pattern has a designated HTML element. We
often need to compose elements and augment them with attributes. Before
you do, though, rummage through the HTML tool chest. Sometimes you might
be surprised by how much is available.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Components Of A Hypermedia System</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_components_of_a_hypermedia_system">Components Of A Hypermedia
System</a>
<ul>
<li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_the_hypermedia">The Hypermedia</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_hypermedia_protocols">Hypermedia Protocols</a>
<ul>
<li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_http_methods">HTTP methods</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_http_response_codes">HTTP response codes</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_caching_http_responses">Caching HTTP responses</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_hypermedia_servers">Hypermedia Servers</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_hypermedia_clients">Hypermedia Clients</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_rest">REST</a>
<ul>
<li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_the_constraints_of_rest">The “Constraints” of REST</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_the_client_server_constraint">The Client-Server Constraint</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_the_statelessness_constraint">The Statelessness Constraint</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_the_caching_constraint">The Caching Constraint</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_the_uniform_interface_constraint">The Uniform Interface
Constraint</a>
<ul>
<li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_identification_of_resources">Identification of resources</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_manipulation_of_resources_through_representations">Manipulation
of resources through representations</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_self_descriptive_messages">Self-descriptive messages</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_hypermedia_as_the_engine_of_application_state_hateoas">Hypermedia
As The Engine of Application State (HATEOAS)</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_hateoas_api_churn">HATEOAS &amp; API churn</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_layered_system">Layered System</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_an_optional_constraint_code_on_demand">An Optional Constraint:
Code-On-Demand</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#_conclusion">Conclusion</a>
</li><li>
<a href="https://hypermedia.systems/components-of-a-hypermedia-system/#html-note-title">HTML Notes: HTML5 Soup</a>
</li></ul>
</details>
<div class="division-content">
<p>A <em class="test">hypermedia system</em> consists of a number of components,
including:</p>
<ul>
<li><p>A hypermedia, such as HTML.</p></li>
<li><p>A network protocol, such as HTTP.</p></li>
<li><p>A server that presents a hypermedia API responding to network
requests with hypermedia responses.</p></li>
<li><p>A client that properly interprets those responses.</p></li>
</ul>
<p>In this chapter we will look at these components and their
implementation in the context of the web.</p>
<p>Once we have reviewed the major components of the web as a hypermedia
system, we will look at some key ideas behind this system — especially
as developed by Roy Fielding in his dissertation, “Architectural Styles
and the Design of Network-based Software Architectures.” We will see
where the terms REpresentational State Transfer (REST), RESTful and
Hypermedia As The Engine Of Application State (HATEOAS) come from, and
we will analyze these terms in the context of the web.</p>
<p>This should give you a stronger understanding of the theoretical
basis of the web as a hypermedia system, how it is supposed to fit
together, and why Hypermedia-Driven Applications are RESTful, whereas
JSON APIs — despite the way the term REST is currently used in the
industry — are not.</p>
<h2 id="_components_of_a_hypermedia_system">Components Of A Hypermedia
System</h2>
<h3 id="_the_hypermedia">The Hypermedia</h3>
<p>The fundamental technology of a hypermedia system is a hypermedia
that allows a client and server to communicate with one another in a
dynamic, non-linear fashion. Again, what makes a hypermedia a hypermedia
is the presence of <em class="test">hypermedia controls</em>: elements that allow
users to select non-linear actions within the hypermedia. Users can
<em class="test">interact</em> with the media in a manner beyond simply reading from
start to end.</p>
<p>We have already mentioned the two primary hypermedia controls in
HTML, anchors and forms, which allow a browser to present links and
operations to a user through a browser.</p>
<p>In the case of HTML, these links and forms typically specify the
target of their operations using <em class="test">Uniform Resource Locators
(URLs)</em>:</p>
<dl>
<dt>Uniform Resource Locator</dt>
<dd>
<p>A uniform resource locator is a textual string that refers to, or
<em class="test">points to</em> a location on a network where a <em class="test">resource</em> can
be retrieved from, as well as the mechanism by which the resource can be
retrieved.</p>
</dd>
</dl>
<p>A URL is a string consisting of various subcomponents:</p>
<figure>
<pre><code>[scheme]://[userinfo]@[host]:[port][path]?[query]#[fragment]
</code></pre>
<figcaption><p>URL Components</p></figcaption>
</figure>
<p>Many of these subcomponents are not required, and are often
omitted.</p>
<p>A typical URL might look like this:</p>
<figure>
<pre><code>https://hypermedia.systems/book/contents/
</code></pre>
<figcaption><p>A simple URL</p></figcaption>
</figure>
<p>This particular URL is made up of the following components:</p>
<ul>
<li><p>A protocol or scheme (in this case, <code>https</code>)</p></li>
<li><p>A domain (e.g., <code>hypermedia.systems</code>)</p></li>
<li><p>A path (e.g., <code>/book/contents</code>)</p></li>
</ul>
<p>This URL uniquely identifies a retrievable <em class="test">resource</em> on the
internet, to which an <em class="test">HTTP Request</em> can be issued by a
hypermedia client that “speaks” HTTPS, such as a web browser. If this
URL is found as the reference of a hypermedia control within an HTML
document, it implies that there is a <em class="test">hypermedia server</em> on the
other side of the network that understands HTTPS as well, and that can
respond to this request with a <em class="test">representation</em> of the given
resource (or redirect you to another location, etc.)</p>
<p>Note that URLs are often not written out entirely within HTML. It is
very common to see anchor tags that look like this, for example:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/book/contents/"</span><span class="dt">&gt;</span>Table Of Contents<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A Simple Link</p></figcaption>
</figure>
<p>Here we have a <em class="test">relative</em> hypermedia reference, where the
protocol, host and port are <em class="test">implied</em> to be that of the “current
document,” that is, the same as whatever the protocol and server were to
retrieve the current HTML page. So, if this link was found in an HTML
document retrieved from <code>https://hypermedia.systems/</code>, then
the implied URL for this anchor would be
<code>https://hypermedia.systems/book/contents/</code>.</p>
<h3 id="_hypermedia_protocols">Hypermedia Protocols</h3>
<p>The hypermedia control (link) above tells a browser: “When a user
clicks on this text, issue a request to
<code>https://hypermedia.systems/book/contents/</code> using the
Hypertext Transfer Protocol,” or HTTP.</p>
<p>HTTP is the <em class="test">protocol</em> used to transfer HTML (hypermedia)
between browsers (hypermedia clients) and servers (hypermedia servers)
and, as such, is the key network technology that binds the distributed
hypermedia system of the web together.</p>
<p>HTTP version 1.1 is a relatively simple network protocol, so lets
take a look at what the <code>GET</code> request triggered by the anchor
tag would look like. This is the request that would be sent to the
server found at <code>hypermedia.systems</code>, on port <code>80</code>
by default:</p>
<figure>
<pre class="http"><code>GET /book/contents/ HTTP/1.1
Accept: text/html,*/*
Host: hypermedia.systems
</code></pre>
</figure>
<p>The first line specifies that this is an HTTP <code>GET</code>
request. It then specifies the path of the resource being requested.
Finally, it contains the HTTP version for this request.</p>
<p>After that are a series of HTTP <em class="test">request headers</em>: individual
lines of name/value pairs separated by a colon. The request headers
provide <em class="test">metadata</em> that can be used by the server to determine
exactly how to respond to the client request. In this case, with the
<code>Accept</code> header, the browser is saying it would prefer HTML
as a response format, but will accept any server response.</p>
<p>Next, it has a <code>Host</code> header that specifies which server
the request has been sent to. This is useful when multiple domains are
hosted on the same host.</p>
<p>An HTTP response from a server to this request might look something
like this:</p>
<figure>
<pre class="http"><code>HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 870
Server: Werkzeug/2.0.2 Python/3.8.10
Date: Sat, 23 Apr 2022 18:27:55 GMT
&lt;html lang="en"&gt;
&lt;body&gt;
&lt;header&gt;
&lt;h1&gt;HYPERMEDIA SYSTEMS&lt;/h1&gt;
&lt;/header&gt;
...
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
</figure>
<p>In the first line, the HTTP Response specifies the HTTP version being
used, followed by a <em class="test">response code</em> of <code>200</code>,
indicating that the given resource was found and that the request
succeeded. This is followed by a string, <code>OK</code> that
corresponds to the response code. (The actual string doesn’t matter, it
is the response code that tells the client the result of a request, as
we will discuss in more detail below.)</p>
<p>After the first line of the response, as with the HTTP Request, we
see a series of <em class="test">response headers</em> that provide metadata to the
client to assist in displaying the <em class="test">representation</em> of the
resource correctly.</p>
<p>Finally, we see some new HTML content. This content is the HTML
<em class="test">representation</em> of the requested resource, in this case a table
of contents of a book. The browser will use this HTML to replace the
entire content in its display window, showing the user this new page,
and updating the address bar to reflect the new URL.</p>
<h4 id="_http_methods">HTTP methods</h4>
<p>The anchor tag above issued an HTTP <code>GET</code>, where
<code>GET</code> is the <em class="test">method</em> of the request. The particular
method being used in an HTTP request is perhaps the most important piece
of information about it, after the actual resource that the request is
directed at.</p>
<p>There are many methods available in HTTP; the ones of most practical
importance to developers are the following:</p>
<dl>
<dt><code>GET</code></dt>
<dd>
<p>A GET request retrieves the representation of the specified resource.
GET requests should not mutate data.</p>
</dd>
<dt><code>POST</code></dt>
<dd>
<p>A POST request submits data to the specified resource. This will
often result in a mutation of state on the server.</p>
</dd>
<dt><code>PUT</code></dt>
<dd>
<p>A PUT request replaces the data of the specified resource. This
results in a mutation of state on the server.</p>
</dd>
<dt><code>PATCH</code></dt>
<dd>
<p>A PATCH request replaces the data of the specified resource. This
results in a mutation of state on the server.</p>
</dd>
<dt><code>DELETE</code></dt>
<dd>
<p>A DELETE request deletes the specified resource. This results in a
mutation of state on the server.</p>
</dd>
</dl>
<p>These methods <em class="test">roughly</em> line up with the
“Create/Read/Update/Delete” or CRUD pattern found in many
applications:</p>
<ul>
<li><p><code>POST</code> corresponds with Creating a resource.</p></li>
<li><p><code>GET</code> corresponds with Reading a resource.</p></li>
<li><p><code>PUT</code> and <code>PATCH</code> correspond with Updating
a resource.</p></li>
<li><p><code>DELETE</code> corresponds, well, with Deleting a
resource.</p></li>
</ul>
<div id="sidebar">
<div>
<div>
<p><strong>Put vs. Post</strong></p>
</div>
<div>
<p>While HTTP Actions correspond roughly to CRUD, they are not the same.
The technical specifications for these methods make no such connection,
and are often somewhat difficult to read. Here, for example, is the
documentation on the distinction between a <code>POST</code> and a
<code>PUT</code> from <a href="https://www.rfc-editor.org/rfc/rfc9110">RFC-9110</a>.</p>
<blockquote>
<p>The target resource in a POST request is intended to handle the
enclosed representation according to the resource’s own semantics,
whereas the enclosed representation in a PUT request is defined as
replacing the state of the target resource. Hence, the intent of PUT is
idempotent and visible to intermediaries, even though the exact effect
is only known by the origin server.</p>
</blockquote><p class="quote-attribution"> RFC-9110, https://www.rfc-editor.org/rfc/rfc9110#section-9.3.4</p>
<p>In plain terms, a <code>POST</code> can be handled by a server pretty
much however it likes, whereas a <code>PUT</code> should be handled as a
“replacement” of the resource, although the language, once again allows
the server to do pretty much whatever it would like within the
constraint of being <a href="https://developer.mozilla.org/en-US/docs/Glossary/Idempotent"><em class="test">idempotent</em></a>.</p>
</div>
</div>
</div>
<p>In a properly structured HTML-based hypermedia system you would use
an appropriate HTTP method for the operation a particular hypermedia
control performs. For example, if a hypermedia control such as a button
<em class="test">deletes</em> a resource, ideally it should issue an HTTP
<code>DELETE</code> request to do so.</p>
<p>A strange thing about HTML, though, is that the native hypermedia
controls can only issue HTTP <code>GET</code> and <code>POST</code>
requests.</p>
<p>Anchor tags always issue a <code>GET</code> request.</p>
<p>Forms can issue either a <code>GET</code> or <code>POST</code> using
the <code>method</code> attribute.</p>
<p>Despite the fact that HTML — the world’s most popular hypermedia —
has been designed alongside HTTP (which is the Hypertext Transfer
Protocol, after all!): if you wish to issue <code>PUT</code>,
<code>PATCH</code> or <code>DELETE</code> requests you currently
<em class="test">have to</em> resort to JavaScript to do so. Since a
<code>POST</code> can do almost anything, it ends up being used for any
mutation on the server, and <code>PUT</code>, <code>PATCH</code> and
<code>DELETE</code> are left aside in plain HTML-based applications.</p>
<p>This is an obvious shortcoming of HTML as a hypermedia; it would be
wonderful to see this fixed in the HTML specification. For now, in
Chapter 4, we’ll discuss ways to get around this.</p>
<h4 id="_http_response_codes">HTTP response codes</h4>
<p>HTTP request methods allow a client to tell a server <em class="test">what</em> to
do to a given resource. HTTP responses contain <em class="test">response codes</em>,
which tell a client what the result of the request was. HTTP response
codes are numeric values that are embedded in the HTTP response, as we
saw above.</p>
<p>The most familiar response code for web developers is probably
<code>404</code>, which stands for “Not Found.” This is the response
code that is returned by web servers when a resource that does not exist
is requested from them.</p>
<p>HTTP breaks response codes up into various categories:</p>
<dl>
<dt><code>100</code>-<code>199</code></dt>
<dd>
<p>Informational responses that provide information about how the server
is processing the response.</p>
</dd>
<dt><code>200</code>-<code>299</code></dt>
<dd>
<p>Successful responses indicating that the request succeeded.</p>
</dd>
<dt><code>300</code>-<code>399</code></dt>
<dd>
<p>Redirection responses indicating that the request should be sent to
some other URL.</p>
</dd>
<dt><code>400</code>-<code>499</code></dt>
<dd>
<p>Client error responses indicating that the client made some sort of
bad request (e.g., asking for something that didn’t exist in the case of
<code>404</code> errors).</p>
</dd>
<dt><code>500</code>-<code>599</code></dt>
<dd>
<p>Server error responses indicating that the server encountered an
error internally as it attempted to respond to the request.</p>
</dd>
</dl>
<p>Within each of these categories there are multiple response codes for
specific situations.</p>
<p>Here are some of the more common or interesting ones:</p>
<dl>
<dt><code>200 OK</code></dt>
<dd>
<p>The HTTP request succeeded.</p>
</dd>
<dt><code>301 Moved Permanently</code></dt>
<dd>
<p>The URL for the requested resource has moved to a new location
permanently, and the new URL will be provided in the
<code>Location</code> response header.</p>
</dd>
<dt><code>302 Found</code></dt>
<dd>
<p>The URL for the requested resource has moved to a new location
temporarily, and the new URL will be provided in the
<code>Location</code> response header.</p>
</dd>
<dt><code>303 See Other</code></dt>
<dd>
<p>The URL for the requested resource has moved to a new location, and
the new URL will be provided in the <code>Location</code> response
header. Additionally, this new URL should be retrieved with a
<code>GET</code> request.</p>
</dd>
<dt><code>401 Unauthorized</code></dt>
<dd>
<p>The client is not yet authenticated (yes, authenticated, despite the
name) and must be authenticated to retrieve the given resource.</p>
</dd>
<dt><code>403 Forbidden</code></dt>
<dd>
<p>The client does not have access to this resource.</p>
</dd>
<dt><code>404 Not Found</code></dt>
<dd>
<p>The server cannot find the requested resource.</p>
</dd>
<dt><code>500 Internal Server Error</code></dt>
<dd>
<p>The server encountered an error when attempting to process the
response.</p>
</dd>
</dl>
<p>There are some fairly subtle differences between HTTP response codes
(and, to be honest, some ambiguities between them). The difference
between a <code>302</code> redirect and a <code>303</code> redirect, for
example, is that the former will issue the request to the new URL using
the same HTTP method as the initial request, whereas the latter will
always use a <code>GET</code>. This is a small but often crucial
difference, as we will see later in the book.</p>
<p>A well crafted Hypermedia-Driven Application will take advantage of
both HTTP methods and HTTP response codes to create a sensible
hypermedia API. You do not want to build a Hypermedia-Driven Application
that uses a <code>POST</code> method for all requests and responds with
<code>200 OK</code> for every response, for example. (Some JSON Data
APIs built on top of HTTP do exactly this!)</p>
<p>When building a Hypermedia-Driven Application, you want, instead, to
go “with the grain” of the web and use HTTP methods and response codes
as they were designed to be used.</p>
<h4 id="_caching_http_responses">Caching HTTP responses</h4>
<p>A constraint of REST (and, therefore, a feature of HTTP) is the
notion of caching responses: a server can indicate to a client (as well
as intermediary HTTP servers) that a given response can be cached for
future requests to the same URL.</p>
<p>The cache behavior of an HTTP response from a server can be indicated
with the <code>Cache-Control</code> response header. This header can
have a number of different values indicating the cacheability of a given
response. If, for example, the header contains the value
<code>max-age=60</code>, this indicates that a client may cache this
response for 60 seconds, and need not issue another HTTP request for
that resource until that time limit has expired.</p>
<p>Another important caching-related response header is
<code>Vary</code>. This response header can be used to indicate exactly
what headers in an HTTP Request form the unique identifier for a cached
result. This becomes important to allow the browser to correctly cache
content in situations where a particular header affects the form of the
server response.</p>
<p>A common pattern in htmx-powered applications, for example, is to use
a custom header set by htmx, <code>HX-Request</code>, to differentiate
between “normal” web requests and requests submitted by htmx. To
properly cache the response to these requests, the
<code>HX-Request</code> request header must be indicated by the
<code>Vary</code> response header.</p>
<p>A full discussion of caching HTTP responses is beyond the scope of
this chapter; see the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching">MDN
Article on HTTP Caching</a> if you would like to know more on the
topic.</p>
<h3 id="_hypermedia_servers">Hypermedia Servers</h3>
<p>Hypermedia servers are any server that can respond to an HTTP request
with an HTTP response. Because HTTP is so simple, this means that nearly
any programming language can be used to build a hypermedia server. There
are a vast number of libraries available for building HTTP-based
hypermedia servers in nearly every programming language imaginable.</p>
<p>This turns out to be one of the best aspects of adopting hypermedia
as your primary technology for building a web application: it removes
the pressure to adopt JavaScript as a backend technology. If you use a
JavaScript-heavy Single Page Application-based front end, and you use
JSON Data APIs, you are going to feel significant pressure to deploy
JavaScript on the back end as well.</p>
<p>In this latter situation, you already have a ton of code written in
JavaScript. Why maintain two separate code bases in two different
languages? Why not create reusable domain logic on the client-side as
well as the server-side? Now that JavaScript has excellent server-side
technologies available like Node and Deno, why not just use a single
language for everything?</p>
<p>In contrast, building a Hypermedia-Driven Application gives you a lot
more freedom in picking the back end technology you want to use. Your
decision can be based on the domain of your application, what languages
and server software you are familiar with or are passionate about, or
just what you feel like trying out.</p>
<p>You certainly aren’t writing your server-side logic in HTML! And
every major programming language has at least one good web framework and
templating library that can be used to handle HTTP requests cleanly.</p>
<p>If you are doing something in big data, perhaps you’d like to use
Python, which has tremendous support for that domain.</p>
<p>If you are doing AI work, perhaps you’d like to use Lisp, leaning on
a language with a long history in that area of research.</p>
<p>Maybe you are a functional programming enthusiast and want to use
OCaml or Haskell. Perhaps you just really like Julia or Nim.</p>
<p>These are all perfectly valid reasons for choosing a particular
server-side technology!</p>
<p>By using hypermedia as your system architecture, you are freed up to
adopt any of these choices. There simply isn’t a large JavaScript code
base on the front end pressuring you to adopt JavaScript on the back
end.</p>
<div id="sidebar">
<div>
<div>
<p><strong>Hypermedia On Whatever you’d Like (HOWL)</strong></p>
</div>
<div>
<p>In the htmx community we call this (with tongue in cheek) the HOWL
stack: Hypermedia On Whatever you’d Like. The htmx community is
multi-language and multi-framework, there are rubyists as well as
pythonistas, lispers as well as haskellers. There are even JavaScript
enthusiasts! All these languages and frameworks are able to adopt
hypermedia, and are able to still share techniques and offer support to
one another because they share a common underlying architecture: they
are all using the web as a hypermedia system.</p>
<p>Hypermedia, in this sense, provides a “universal language” for the
web that we can all use.</p>
</div>
</div>
</div>
<h3 id="_hypermedia_clients">Hypermedia Clients</h3>
<p>We now come to the final major component in a hypermedia system: the
hypermedia client. Hypermedia <em class="test">clients</em> are software that
understand how to interpret a particular hypermedia, and the hypermedia
controls within it, properly. The canonical example, of course, is the
web browser, which understands HTML and can present it to a user to
interact with. Web browsers are incredibly sophisticated pieces of
software. (So sophisticated, in fact, that they are often re-purposed
away from being a hypermedia client, to being a sort of cross-platform
virtual machine for launching Single Page Applications.)</p>
<p>Browsers aren’t the only hypermedia clients out there, however. In
the last section of this book we will look at Hyperview, a
mobile-oriented hypermedia. One of the outstanding features of Hyperview
is that it doesn’t simply provide a hypermedia, HXML, but also provides
a <em class="test">working hypermedia client</em> for that hypermedia. This makes
building a proper Hypermedia-Driven Application with Hyperview extremely
easy.</p>
<p>A crucial feature of a hypermedia system is what is known as <em class="test">the
uniform interface</em>. We discuss this concept in depth in the next
section on REST. What is often ignored in discussions about hypermedia
is how important the hypermedia client is in taking advantage of this
uniform interface. A hypermedia client must know how to properly
interpret and present hypermedia controls found in a hypermedia response
from a hypermedia server for the whole hypermedia system to hang
together. Without a sophisticated client that can do this, hypermedia
controls and a hypermedia-based API are much less useful.</p>
<p>This is one reason why JSON APIs have rarely adopted hypermedia
controls successfully: JSON APIs are typically consumed by code that is
expecting a fixed format and that isn’t designed to be a hypermedia
client. This is totally understandable: building a good hypermedia
client is hard! For JSON API clients like this, the power of hypermedia
controls embedded within an API response is irrelevant and often simply
annoying:</p>
<blockquote>
<p>The short answer to this question is that HATEOAS isn’t a good fit
for most modern use cases for APIs. That is why after almost 20 years,
HATEOAS still hasn’t gained wide adoption among developers. GraphQL on
the other hand is spreading like wildfire because it solves real-world
problems.</p>
</blockquote><p class="quote-attribution"> Freddie Karlbom,
https://techblog.commercetools.com/graphql-and-rest-level-3-hateoas-70904ff1f9cf</p>
<p>HATEOAS will be described in more detail below, but the takeaway here
is that a good hypermedia client is a necessary component within a
larger hypermedia system.</p>
<h2 id="_rest">REST</h2>
<p>Now that we have reviewed the major components of a hypermedia
system, it’s time to look more deeply into the concept of REST. The term
“REST” comes from Roy Fielding’s PhD dissertation on the architecture of
the web. Fielding wrote his dissertation at U.C. Irvine, after having
helped build much of the infrastructure of the early web, including the
Apache web server. Roy was attempting to formalize and describe the
novel distributed computing system that he had helped to build.</p>
<p>We are going to focus on what we feel is the most important section
of Fielding’s writing, from a web development perspective: Section 5.1.
This section contains the core concepts (Fielding calls them
<em class="test">constraints</em>) of Representational State Transfer, or REST.</p>
<p>Before we get into the muck, however, it is important to understand
that Fielding discusses REST as a <em class="test">network architecture</em>, that
is, as an entirely different way to architect a distributed system. And,
further, as a novel network architecture that should be
<em class="test">contrasted</em> with earlier approaches to distributed systems.</p>
<p>It is also important to emphasize that, at the time Fielding wrote
his dissertation, JSON APIs and AJAX did not exist. He was describing
the early web, with HTML being transferred over HTTP by early browsers,
as a hypermedia system.</p>
<p>Today, in a strange turn of events, the term “REST” is mainly
associated with JSON Data APIs, rather than with HTML and hypermedia.
This is extremely funny once you realize that the vast majority of JSON
Data APIs aren’t RESTful, in the original sense, and, in fact,
<em class="test">can’t</em> be RESTful, since they aren’t using a natural hypermedia
format.</p>
<p>To re-emphasize: REST, as coined by Fielding, describes the
<em class="test">pre-API web</em>, and letting go of the current, common usage of the
term REST to simply mean “a JSON API” is necessary to develop a proper
understanding of the idea.</p>
<h3 id="_the_constraints_of_rest">The “Constraints” of REST</h3>
<p>In his dissertation, Fielding defines various “constraints” to
describe how a RESTful system must behave. This approach can feel a
little round-about and difficult to follow for many people, but it is an
appropriate approach for an academic document. Given a bit of time
thinking about the constraints he outlines and some concrete examples of
those constraints it will become easy to assess whether a given system
actually satisfies the architectural requirements of REST or not.</p>
<p>Here are the constraints of REST Fielding outlines:</p>
<ul>
<li><p>It is a client-server architecture (section 5.1.2).</p></li>
<li><p>It must be stateless; (section 5.1.3) that is, every request
contains all information necessary to respond to that request.</p></li>
<li><p>It must allow for caching (section 5.1.4).</p></li>
<li><p>It must have a <em class="test">uniform interface</em> (section
5.1.5).</p></li>
<li><p>It is a layered system (section 5.1.6).</p></li>
<li><p>Optionally, it can allow for Code-On-Demand (section 5.1.7), that
is, scripting.</p></li>
</ul>
<p>Let’s go through each of these constraints in turn and discuss them
in detail, looking at how (and to what extent) the web satisfies each of
them.</p>
<h3 id="_the_client_server_constraint">The Client-Server Constraint</h3>
<p>See <a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_2">Section
5.1.2</a> for the Client-Server constraint.</p>
<p>The REST model Fielding was describing involved both <em class="test">clients</em>
(browsers, in the case of the web) and <em class="test">servers</em> (such as the
Apache Web Server he had been working on) communicating via a network
connection. This was the context of his work: he was describing the
network architecture of the World Wide Web, and contrasting it with
earlier architectures, notably thick-client networking models such as
the Common Object Request Broker Architecture (CORBA).</p>
<p>It should be obvious that any web application, regardless of how it
is designed, will satisfy this requirement.</p>
<h3 id="_the_statelessness_constraint">The Statelessness Constraint</h3>
<p>See <a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_3">Section
5.1.3</a> for the Stateless constraint.</p>
<p>As described by Fielding, a RESTful system is stateless: every
request should encapsulate all information necessary to respond to that
request, with no side state or context stored on either the client or
the server.</p>
<p>In practice, for many web applications today, we actually violate
this constraint: it is common to establish a <em class="test">session cookie</em>
that acts as a unique identifier for a given user and that is sent along
with every request. While this session cookie is, by itself, not
stateful (it is sent with every request), it is typically used as a key
to look up information stored on the server, in what is usually termed
“the session.”</p>
<p>This session information is typically stored in some sort of shared
storage across multiple web servers, holding things like the current
user’s email or id, their roles, partially created domain objects,
caches, and so forth.</p>
<p>This violation of the Statelessness REST architectural constraint has
proven to be useful for building web applications and does not appear to
have had a major impact on the overall flexibility of the web. But it is
worth bearing in mind that even Web 1.0 applications often violate the
purity of REST in the interest of pragmatic trade-offs.</p>
<p>And it must be said that sessions <em class="test">do</em> cause additional
operational complexity headaches when deploying hypermedia servers;
these may need shared access to session state information stored across
an entire cluster. So Fielding was correct in pointing out that an ideal
RESTful system, one that did not violate this constraint, would be
simpler and therefore more robust.</p>
<h3 id="_the_caching_constraint">The Caching Constraint</h3>
<p>See <a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_4">Section
5.1.4</a> for the Caching constraint.</p>
<p>This constraint states that a RESTful system should support the
notion of caching, with explicit information on the cache-ability of
responses for future requests of the same resource. This allows both
clients as well as intermediary servers between a given client and final
server to cache the results of a given request.</p>
<p>As we discussed earlier, HTTP has a sophisticated caching mechanism
via response headers that is often overlooked or underutilized when
building hypermedia applications. Given the existence of this
functionality, however, it is easy to see how this constraint is
satisfied by the web.</p>
<h3 id="_the_uniform_interface_constraint">The Uniform Interface
Constraint</h3>
<p>Now we come to the most interesting and, in our opinion, most
innovative constraint in REST: that of the <em class="test">uniform
interface</em>.</p>
<p>This constraint is the source of much of the <em class="test">flexibility</em> and
<em class="test">simplicity</em> of a hypermedia system, so we are going to spend
some time on it.</p>
<p>See <a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5">Section
5.1.5</a> for the Uniform Interface constraint.</p>
<p>In this section, Fielding says:</p>
<blockquote>
<p>The central feature that distinguishes the REST architectural style
from other network-based styles is its emphasis on a uniform interface
between components…​ In order to obtain a uniform interface, multiple
architectural constraints are needed to guide the behavior of
components. REST is defined by four interface constraints:
identification of resources; manipulation of resources through
representations; self-descriptive messages; and, hypermedia as the
engine of application state</p>
</blockquote><p class="quote-attribution"> Roy Fielding, Architectural Styles and the Design of Network-based
Software Architectures</p>
<p>So we have four sub-constraints that, taken together, form the
Uniform Interface constraint.</p>
<h4 id="_identification_of_resources">Identification of resources</h4>
<p>In a RESTful system, resources should have a unique identifier. Today
the concept of Universal Resource Locators (URLs) is common, but at the
time of Fielding’s writing they were still relatively new and novel.</p>
<p>What might be more interesting today is the notion of a
<em class="test">resource</em>, thus being identified: in a RESTful system,
<em class="test">any</em> sort of data that can be referenced, that is, the target of
a hypermedia reference, is considered a resource. URLs, though common
enough today, end up solving the very complex problem of uniquely
identifying any and every resource on the internet.</p>
<h4 id="_manipulation_of_resources_through_representations">Manipulation
of resources through representations</h4>
<p>In a RESTful system, <em class="test">representations</em> of the resource are
transferred between clients and servers. These representations can
contain both data and metadata about the request (such as “control data”
like an HTTP method or response code). A particular data format or
<em class="test">media type</em> may be used to present a given resource to a client,
and that media type can be negotiated between the client and the
server.</p>
<p>We saw this latter aspect of the uniform interface in the
<code>Accept</code> header in the requests above.</p>
<h4 id="_self_descriptive_messages">Self-descriptive messages</h4>
<p>The Self-Descriptive Messages constraint, combined with the next one,
HATEOAS, form what we consider to be the core of the Uniform Interface,
of REST and why hypermedia provides such a powerful system
architecture.</p>
<p>The Self-Descriptive Messages constraint requires that, in a RESTful
system, messages must be <em class="test">self-describing</em>.</p>
<p>This means that <em class="test">all information</em> necessary to both display
<em class="test">and also operate</em> on the data being represented must be present
in the response. In a properly RESTful system, there can be no
additional “side” information necessary for a client to transform a
response from a server into a useful user interface. Everything must “be
in” the message itself, in the form of hypermedia controls.</p>
<p>This might sound a little abstract so let’s look at a concrete
example.</p>
<p>Consider two different potential responses from an HTTP server for
the URL <code>https://example.com/contacts/42</code>.</p>
<p>Both responses will return information about a contact, but each
response will take very different forms.</p>
<p>The first implementation returns an HTML representation:</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">html</span><span class="ot"> lang</span><span class="op">=</span><span class="st">"en"</span><span class="dt">&gt;</span></span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">h1</span><span class="dt">&gt;</span>Joe Smith<span class="dt">&lt;/</span><span class="kw">h1</span><span class="dt">&gt;</span></span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb6-5"><a aria-hidden="true" href="#cb6-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Email: joe@example.bar<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb6-6"><a aria-hidden="true" href="#cb6-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Status: Active<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb6-7"><a aria-hidden="true" href="#cb6-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb6-8"><a aria-hidden="true" href="#cb6-8" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb6-9"><a aria-hidden="true" href="#cb6-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/42/archive"</span><span class="dt">&gt;</span>Archive<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb6-10"><a aria-hidden="true" href="#cb6-10" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb6-11"><a aria-hidden="true" href="#cb6-11" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb6-12"><a aria-hidden="true" href="#cb6-12" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">html</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>The second implementation returns a JSON representation:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a> <span class="dt">"name"</span><span class="fu">:</span> <span class="st">"Joe Smith"</span><span class="fu">,</span></span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a> <span class="dt">"email"</span><span class="fu">:</span> <span class="st">"joe@example.org"</span><span class="fu">,</span></span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a> <span class="dt">"status"</span><span class="fu">:</span> <span class="st">"Active"</span></span>
<span id="cb7-5"><a aria-hidden="true" href="#cb7-5" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
</figure>
<p>What can we say about the differences between these two
responses?</p>
<p>One thing that may initially jump out at you is that the JSON
representation is smaller than the HTML representation. Fielding notes
exactly this trade-off when using a RESTful architecture:</p>
<blockquote>
<p>The trade-off, though, is that a uniform interface degrades
efficiency, since information is transferred in a standardized form
rather than one which is specific to an application’s needs.</p>
</blockquote><p class="quote-attribution"> Roy Fielding, Architectural Styles and the Design of Network-based
Software Architectures</p>
<p>So REST <em class="test">trades off</em> representational efficiency for other
goals.</p>
<p>To understand these other goals, first notice that the HTML
representation has a hyperlink in it to navigate to a page to archive
the contact. The JSON representation, in contrast, does not have this
link.</p>
<p>What are the ramifications of this fact for a <em class="test">client</em> of the
JSON API?</p>
<p>What this means is that the JSON API client must know <em class="test">in
advance</em> exactly what other URLs (and request methods) are available
for working with the contact information. If the JSON client is able to
update this contact in some way, it must know how to do so from some
source of information <em class="test">external</em> to the JSON message. If the
contact has a different status, say “Archived”, does this change the
allowable actions? If so, what are the new allowable actions?</p>
<p>The source of all this information might be API documentation, word
of mouth or, if the developer controls both the server and the client,
internal knowledge. But this information is implicit and
<em class="test">outside</em> the response.</p>
<p>Contrast this with the hypermedia (HTML) response. In this case, the
hypermedia client (that is, the browser) needs only to know how to
render the given HTML. It doesn’t need to understand what actions are
available for this contact: they are simply encoded <em class="test">within</em> the
HTML response itself as hypermedia controls. It doesn’t need to
understand what the status field means. In fact, the client doesn’t even
know what a contact is!</p>
<p>The browser, our hypermedia client, simply renders the HTML and
allows the user, who presumably understands the concept of a Contact, to
make a decision on what action to pursue from the actions made available
in the representation.</p>
<p>This difference between the two responses demonstrates the crux of
REST and hypermedia, what makes them so powerful and flexible: clients
(again, web browsers) don’t need to understand <em class="test">anything</em> about
the underlying resources being represented.</p>
<p>Browsers only (only! As if it is easy!) need to understand how to
interpret and display hypermedia, in this case HTML. This gives
hypermedia-based systems unprecedented flexibility in dealing with
changes to both the backing representations and to the system
itself.</p>
<h4 id="_hypermedia_as_the_engine_of_application_state_hateoas">Hypermedia
As The Engine of Application State (HATEOAS)</h4>
<p>The final sub-constraint on the Uniform Interface is that, in a
RESTful system, hypermedia should be “the engine of application state.”
This is sometimes abbreviated as “HATEOAS”, although Fielding prefers to
use the terminology “the hypermedia constraint” when discussing it.</p>
<p>This constraint is closely related to the previous self-describing
message constraint. Let us consider again the two different
implementations of the endpoint <code>/contacts/42</code>, one returning
HTML and one returning JSON. Let’s update the situation such that the
contact identified by this URL has now been archived.</p>
<p>What do our responses look like?</p>
<p>The first implementation returns the following HTML:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">html</span><span class="ot"> lang</span><span class="op">=</span><span class="st">"en"</span><span class="dt">&gt;</span></span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">h1</span><span class="dt">&gt;</span>Joe Smith<span class="dt">&lt;/</span><span class="kw">h1</span><span class="dt">&gt;</span></span>
<span id="cb8-4"><a aria-hidden="true" href="#cb8-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-5"><a aria-hidden="true" href="#cb8-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Email: joe@example.bar<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-6"><a aria-hidden="true" href="#cb8-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Status: Archived<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-7"><a aria-hidden="true" href="#cb8-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb8-8"><a aria-hidden="true" href="#cb8-8" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb8-9"><a aria-hidden="true" href="#cb8-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/42/unarchive"</span><span class="dt">&gt;</span>Unarchive<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb8-10"><a aria-hidden="true" href="#cb8-10" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb8-11"><a aria-hidden="true" href="#cb8-11" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb8-12"><a aria-hidden="true" href="#cb8-12" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">html</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>The second implementation returns the following JSON
representation:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> <span class="dt">"name"</span><span class="fu">:</span> <span class="st">"Joe Smith"</span><span class="fu">,</span></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a> <span class="dt">"email"</span><span class="fu">:</span> <span class="st">"joe@example.org"</span><span class="fu">,</span></span>
<span id="cb9-4"><a aria-hidden="true" href="#cb9-4" tabindex="-1"></a> <span class="dt">"status"</span><span class="fu">:</span> <span class="st">"Archived"</span></span>
<span id="cb9-5"><a aria-hidden="true" href="#cb9-5" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
</figure>
<p>The important point to notice here is that, by virtue of being a
self-describing message, the HTML response now shows that the “Archive”
operation is no longer available, and a new “Unarchive” operation has
become available. The HTML representation of the contact
<em class="test">encodes</em> the state of the application; it encodes exactly what
can and cannot be done with this particular representation, in a way
that the JSON representation does not.</p>
<p>A client interpreting the JSON response must, again, understand not
only the general concept of a Contact, but also specifically what the
“status” field with the value “Archived” means. It must know exactly
what operations are available on an “Archived” contact, to appropriately
display them to an end user. The state of the application is not encoded
in the response, but rather conveyed through a mix of raw data and side
channel information such as API documentation.</p>
<p>Furthermore, in the majority of front end SPA frameworks today, this
contact information would live <em class="test">in memory</em> in a JavaScript object
representing a model of the contact, while the page data is held in the
browser’s <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model">Document
Object Model</a> (DOM). The DOM would be updated based on changes to
this model, that is, the DOM would “react” to changes to this backing
JavaScript model.</p>
<p>This approach is certainly <em class="test">not</em> using Hypermedia As The
Engine Of Application State: rather, it is using a JavaScript model as
the engine of application state, and synchronizing that model with a
server and with the browser.</p>
<p>With the HTML approach, the Hypermedia is, indeed, The Engine Of
Application State: there is no additional model on the client side, and
all state is expressed directly in the hypermedia, in this case HTML. As
state changes on the server, it is reflected in the representation (that
is, HTML) sent back to the client. The hypermedia client (a browser)
doesn’t know anything about contacts, what the concept of “Archiving”
is, or anything else about the particular domain model for this
response: it simply knows how to render HTML.</p>
<p>Because a hypermedia client doesn’t need to know anything about the
server model beyond how to render hypermedia to a client, it is
incredibly flexible with respect to the representations it receives and
displays to users.</p>
<h4 id="_hateoas_api_churn">HATEOAS &amp; API churn</h4>
<p>This last point is critical to understanding the flexibility of
hypermedia, so let’s look at a practical example of it in action.
Consider a situation where a new feature has been added to the web
application with these two end points. This feature allows you to send a
message to a given Contact.</p>
<p>How would this change each of the two responses—​HTML and JSON—​from
the server?</p>
<p>The HTML representation might now look like this:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">html</span><span class="ot"> lang</span><span class="op">=</span><span class="st">"en"</span><span class="dt">&gt;</span></span>
<span id="cb10-2"><a aria-hidden="true" href="#cb10-2" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb10-3"><a aria-hidden="true" href="#cb10-3" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">h1</span><span class="dt">&gt;</span>Joe Smith<span class="dt">&lt;/</span><span class="kw">h1</span><span class="dt">&gt;</span></span>
<span id="cb10-4"><a aria-hidden="true" href="#cb10-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb10-5"><a aria-hidden="true" href="#cb10-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Email: joe@example.bar<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb10-6"><a aria-hidden="true" href="#cb10-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Status: Active<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb10-7"><a aria-hidden="true" href="#cb10-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb10-8"><a aria-hidden="true" href="#cb10-8" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb10-9"><a aria-hidden="true" href="#cb10-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/42/archive"</span><span class="dt">&gt;</span>Archive<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb10-10"><a aria-hidden="true" href="#cb10-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/42/message"</span><span class="dt">&gt;</span>Message<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb10-11"><a aria-hidden="true" href="#cb10-11" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb10-12"><a aria-hidden="true" href="#cb10-12" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb10-13"><a aria-hidden="true" href="#cb10-13" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">html</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>The JSON representation, on the other hand, might look like this:</p>
<figure>
<div class="sourceCode" id="cb11"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a> <span class="dt">"name"</span><span class="fu">:</span> <span class="st">"Joe Smith"</span><span class="fu">,</span></span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> <span class="dt">"email"</span><span class="fu">:</span> <span class="st">"joe@example.org"</span><span class="fu">,</span></span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a> <span class="dt">"status"</span><span class="fu">:</span> <span class="st">"Active"</span></span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
</figure>
<p>Note that, once again, the JSON representation is unchanged. There is
no indication of this new functionality. Instead, a client must
<em class="test">know</em> about this change, presumably via some shared
documentation between the client and the server.</p>
<p>Contrast this with the HTML response. Because of the uniform
interface of the RESTful model and, in particular, because we are using
Hypermedia As The Engine of Application State, no such exchange of
documentation is necessary! Instead, the client (a browser) simply
renders the new HTML with this operation in it, making this operation
available for the end user without any additional coding changes.</p>
<p>A pretty neat trick!</p>
<p>Now, in this case, if the JSON client is not properly updated, the
error state is relatively benign: a new bit of functionality is simply
not made available to users. But consider a more severe change to the
API: what if the archive functionality was removed? Or what if the URLs
or the HTTP methods for these operations changed in some way?</p>
<p>In this case, the JSON client may be broken in a much more serious
manner.</p>
<p>The HTML response, however, would simply be updated to exclude the
removed options or to update the URLs used for them. Clients would see
the new HTML, display it properly, and allow users to select whatever
the new set of operations happens to be. Once again, the uniform
interface of REST has proven to be extremely flexible: despite a
potentially radically new layout for our hypermedia API, clients
continue to work.</p>
<p>An important fact emerges from this: due to this flexibility,
hypermedia APIs <em class="test">do not have the versioning headaches that JSON Data
APIs do</em>.</p>
<p>Once a Hypermedia-Driven Application has been “entered into” (that
is, loaded through some entry point URL), all functionality and
resources are surfaced through self-describing messages. Therefore,
there is no need to exchange documentation with the client: the client
simply renders the hypermedia (in this case HTML) and everything works
out. When a change occurs, there is no need to create a new version of
the API: clients simply retrieve updated hypermedia, which encodes the
new operations and resources in it, and display it to users to work
with.</p>
<h3 id="_layered_system">Layered System</h3>
<p>The final “required” constraint on a RESTful system that we will
consider is The Layered System constraint. This constraint can be found
in <a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_6">Section
5.1.6</a> of Fielding’s dissertation.</p>
<p>To be frank, after the excitement of the uniform interface
constraint, the “layered system” constraint is a bit of a let down. But
it is still worth understanding and it is actually utilized effectively
by The web. The constraint requires that a RESTful architecture be
“layered,” allowing for multiple servers to act as intermediaries
between a client and the eventual “source of truth” server.</p>
<p>These intermediary servers can act as proxies, transform intermediate
requests and responses and so forth.</p>
<p>A common modern example of this layering feature of REST is the use
of Content Delivery Networks (CDNs) to deliver unchanging static assets
to clients more quickly, by storing the response from the origin server
in intermediate servers more closely located to the client making a
request.</p>
<p>This allows content to be delivered more quickly to the end user and
reduces load on the origin server.</p>
<p>Not as exciting for web application developers as the uniform
interface, at least in our opinion, but useful nonetheless.</p>
<h3 id="_an_optional_constraint_code_on_demand">An Optional Constraint:
Code-On-Demand</h3>
<p>We called The Layered System constraint the final “required”
constraint because Fielding mentions one additional constraint on a
RESTful system. This Code On Demand constraint is somewhat awkwardly
described as “optional” (Section 5.1.7).</p>
<p>In this section, Fielding says:</p>
<blockquote>
<p>REST allows client functionality to be extended by downloading and
executing code in the form of applets or scripts. This simplifies
clients by reducing the number of features required to be
pre-implemented. Allowing features to be downloaded after deployment
improves system extensibility. However, it also reduces visibility, and
thus is only an optional constraint within REST.</p>
</blockquote><p class="quote-attribution"> Roy Fielding, Architectural Styles and the Design of Network-based
Software Architectures</p>
<p>So, scripting was and is a native aspect of the original RESTful
model of the web, and thus should of course be allowed in a
Hypermedia-Driven Application.</p>
<p>However, in a Hypermedia-Driven Application the presence of scripting
should <em class="test">not</em> change the fundamental networking model: hypermedia
should continue to be the engine of application state, server
communication should still consist of hypermedia exchanges rather than,
for example, JSON data exchanges, and so on. (JSON Data API’s certainly
have their place; in Chapter 10 we’ll discuss when and how to use
them).</p>
<p>Today, unfortunately, the scripting layer of the web, JavaScript, is
quite often used to <em class="test">replace</em>, rather than augment the hypermedia
model. We will elaborate in a later chapter what scripting that does not
replace the underlying hypermedia system of the web looks like.</p>
<h2 id="_conclusion">Conclusion</h2>
<p>After this deep dive into the components and concepts behind
hypermedia systems — including Roy Fielding’s insights into their
operation — we hope you have much better understanding of REST, and in
particular, of the uniform interface and HATEOAS. We hope you can see
<em class="test">why</em> these characteristics make hypermedia systems so
flexible.</p>
<p>If you were not aware of the full significance of REST and HATEOAS
before now, don’t feel bad: it took some of us over a decade of working
in web development, and building a hypermedia-oriented library to boot,
to understand the special nature of HTML, hypermedia and the web!</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: HTML5 Soup</h2>
<blockquote>
<p>The beginning of wisdom is to call things by their right names.</p>
</blockquote><p class="quote-attribution"> Confucius</p>
<p>Elements like <code>&lt;section&gt;</code>,
<code>&lt;article&gt;</code>, <code>&lt;nav&gt;</code>,
<code>&lt;header&gt;</code>, <code>&lt;footer&gt;</code>,
<code>&lt;figure&gt;</code> have become a sort of shorthand for
HTML.</p>
<p>By using these elements, a page can make false promises, like
<code>&lt;article&gt;</code> elements being self-contained, reusable
entities, to clients like browsers, search engines and scrapers that
can’t know better. To avoid this:</p>
<ul>
<li><p>Make sure that the element you’re using fits your use case. Check
the HTML spec.</p></li>
<li><p>Don’t try to be specific when you can’t or don’t need to.
Sometimes, <code>&lt;div&gt;</code> is fine.</p></li>
</ul>
<p>The most authoritative resource for learning about HTML is the HTML
specification. The current specification lives on <a href="https://html.spec.whatwg.org/multipage">https://html.spec.whatwg.org/multipage</a>.<a class="footnote-ref" href="#fn1" id="fnref1" role="doc-noteref"><sup>1</sup></a> There’s no need to rely on hearsay
to keep up with developments in HTML.</p>
<p>Section 4 of the spec features a list of all available elements,
including what they represent, where they can occur, and what they are
allowed to contain. It even tells you when you’re allowed to leave out
closing tags!</p>
</div>
</div>
<section class="footnotes footnotes-end-of-document" id="footnotes" role="doc-endnotes">
<hr/>
<ol>
<li id="fn1"><p>The single-page version is too slow to load and render
on most computers. There’s also a “developers’ edition” at /dev, but the
standard version has nicer styling.<a class="footnote-back" href="#fnref1" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">A Web 1.0 Application</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_picking_a_web_stack">Picking A “Web Stack”</a>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_python">Python</a>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_introducing_flask_our_first_route">Introducing Flask: Our First
Route</a>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_contact_app_functionality">Contact.app Functionality</a>
<ul>
<li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_showing_a_searchable_list_of_contacts">Showing A Searchable
List Of Contacts</a>
<ul>
<li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_the_list_search_templates">The list &amp; search templates</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_adding_a_new_contact">Adding A New Contact</a>
<ul>
<li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_handling_the_post_to_contactsnew">Handling the post to
/contacts/new</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_viewing_the_details_of_a_contact">Viewing The Details Of A
Contact</a>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_the_contact_detail_template">The Contact Detail Template</a>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_editing_and_deleting_a_contact">Editing And Deleting A
Contact</a>
<ul>
<li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_handling_the_post_to_contactscontact_id">Handling the post to
/contacts/&lt;contact_id&gt;/edit</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_deleting_a_contact">Deleting A Contact</a>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#_contact_app_implemented">Contact.app…​ Implemented!</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/a-web-1-0-application/#html-note-title">HTML Notes: Framework Soup</a>
</li></ul>
</details>
<div class="division-content">
<p>To start our journey into Hypermedia-Driven Applications, we are
going to create a simple contact management web application called
Contact.app. We will start with a basic, “Web 1.0-style” Multi-Page
Application (MPA), in the grand CRUD (Create, Read, Update, Delete)
tradition. It will not be the best contact management application in the
world, but it will be simple and it will do its job.</p>
<p>This application will also be easy to incrementally improve in the
coming chapters by utilizing the hypermedia-oriented library htmx.</p>
<p>By the time we are finished building and enhancing the application,
over the next few chapters, it will have some very slick features that
most developers today would assume requires the use of a SPA JavaScript
framework.</p>
<h2 id="_picking_a_web_stack">Picking A “Web Stack”</h2>
<p>In order to demonstrate how web 1.0 applications work, we need to
pick a server-side language and a library for handling HTTP requests.
Colloquially, this is called our “Server-Side” or “Web” stack, and there
are literally hundreds of options to choose from, many with passionate
followings. You probably have a web framework that you prefer and, while
we wish we could write this book for every possible stack out there, in
the interest of simplicity (and sanity) we can only pick one.</p>
<p>For this book we are going to use the following stack:</p>
<ul>
<li><p><a href="https://www.python.org/">Python</a> as our programming
language.</p></li>
<li><p><a href="https://palletsprojects.com/p/flask/">Flask</a> as our
web framework, allowing us to connect HTTP requests to Python
logic.</p></li>
<li><p><a href="https://palletsprojects.com/p/jinja/">Jinja2</a> for our
server-side templating language, allowing us to render HTML responses
using a familiar and intuitive syntax.</p></li>
</ul>
<p>Why this particular stack?</p>
<p>Python is the most popular programming language in the world, as of
this writing, according to the <a href="https://www.tiobe.com/tiobe-index/">TIOBE index</a>, a respected
measure of programming language popularity. More importantly, Python is
easy to read even if you aren’t familiar with it.</p>
<p>We chose the Flask web framework because it is simple and does not
impose a lot of structure on top of the basics of HTTP request
handling.</p>
<p>This bare-bones approach is a good match for our needs: in other
cases you might consider a more full-featured Python framework, such as
<a href="https://www.djangoproject.com/">Django</a>, which supplies much
more functionality out of the box than Flask does.</p>
<p>By using Flask for our book, we will be able to keep our code focused
on <em class="test">hypermedia exchanges</em>.</p>
<p>We picked Jinja2 templates because they are the default templating
language for Flask. They are simple enough and similar enough to most
other server-side templating languages that most people who are familiar
with any server-side (or client-side) templating library should be able
to understand them quickly and easily.</p>
<p>Even if this combination of technologies isn’t your preferred stack,
please, keep reading: you will learn quite a bit from the patterns we
introduce in the coming chapters and it shouldn’t be hard to map them
into your preferred language and frameworks.</p>
<p>With this stack we will be rendering HTML <em class="test">on the server-side</em>
to return to clients, rather than producing JSON. This is the
traditional approach to building web applications. However, with the
rise of SPAs, this approach is not as widely used a technique as it once
was. Today, as people are rediscovering this style of web applications,
the term “Server-Side Rendering” or SSR is emerging as the way that
people talk about it. This contrasts with “Client-Side Rendering”, that
is, rendering templates in the browser with data retrieved in JSON form
from the server, as is common in SPA libraries.</p>
<p>In Contact.app we will intentionally keep things as simple as
possible to maximize the teaching value of our code: it won’t be
perfectly factored code, but it will be easy to follow for readers, even
if they have little Python experience, and it should be easy to
translate both the application and the techniques demonstrated into your
preferred programming environment.</p>
<h2 id="_python">Python</h2>
<p>Since this book is for learning how to use hypermedia effectively,
we’ll just briefly introduce the various technologies we use
<em class="test">around</em> that hypermedia. This has some obvious drawbacks: if you
aren’t comfortable with Python, for example, some example Python code in
the book may be a bit confusing or mysterious at first.</p>
<p>If you feel like you need a quick introduction to the language before
diving into the code, we recommend the following books and websites:</p>
<ul>
<li><p><a href="https://nostarch.com/python-crash-course-3rd-edition">Python Crash
Course</a> from No Starch Press</p></li>
<li><p><a href="https://learnpythonthehardway.org/python3/">Learn Python
The Hard Way</a> by Zed Shaw</p></li>
<li><p><a href="https://www.py4e.com/">Python For Everybody</a> by Dr.
Charles R. Severance</p></li>
</ul>
<p>We think most web developers, even developers who are unfamiliar with
Python, should be able to follow along with our examples. Most of the
authors of this book hadn’t written much Python before writing it, and
we got the hang of it pretty quickly.</p>
<h2 id="_introducing_flask_our_first_route">Introducing Flask: Our First
Route</h2>
<p>Flask is a simple but flexible web framework for Python. We’ll ease
into it by touching on its core elements.</p>
<p>A Flask application consists of a series of <em class="test">routes</em> tied to
functions that execute when an HTTP request to a given path is made. It
uses a Python feature called “decorators” to declare the route that will
be handled, which is then followed by a function to handle requests to
that route. We’ll use the term “handler” to refer to the functions
associated with a route.</p>
<p>Let’s create our first route definition, a simple “Hello World”
route. In the following Python code you will see the <code>@app</code>
symbol. This is the flask decorator that allows us to set up our routes.
Don’t worry too much about how decorators work in Python, just know that
this feature allows us to map a given <em class="test">path</em> to a particular
function (i.e., handler). The Flask application, when started, will take
HTTP requests and look up the matching handler and invoke it.</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/"</span>) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a><span class="kw">def</span> index(): <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a> <span class="cf">return</span> <span class="st">"Hello World!"</span> <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>A simple “Hello World” route</p></figcaption>
</figure>
<ol>
<li><p>Establishes we are mapping the <code>/</code> path as a
route.</p></li>
<li><p>The next method is the handler for that route.</p></li>
<li><p>Returns the string “Hello World!” to the client.</p></li>
</ol>
<p>The <code>route()</code> method on the Flask decorator takes an
argument: the path you wish the route to handle. Here we pass in the
root or <code>/</code> path, as a string, to handle requests to the root
path.</p>
<p>This route declaration is then followed by a simple function
definition, <code>index()</code>. In Python, decorators invoked in this
manner apply to the function immediately following them. Therefore, this
function becomes the “handler” for that route, and will be executed when
an HTTP request to the given path is made.</p>
<p>Note that the name of the function doesn’t matter, we can call it
whatever we’d like so long as it is unique. In this case we chose
<code>index()</code> because that fits with the route we are handling:
the root “index” of the web application.</p>
<p>So we have the <code>index()</code> function immediately following
our route definition for the root, and this will become the handler for
the root URL in our web application.</p>
<p>The handler in this case is dead simple, it just returns a string,
“Hello World!”, to the client. This isn’t hypermedia yet, but as we can
see in <a class="ref" href="#fig-helloworld">[fig-helloworld]</a>, a
browser will render it just fine.</p>
<figure id="fig-helloworld">
<p><img src="https://hypermedia.systems/images/figure_2-1_hello_world.png"/></p>
<figcaption><p>Hello World!</p></figcaption>
</figure>
<p>Great, there’s our first step into Flask, showing the core technique
we are going to use to respond to HTTP requests: routes mapped to
handlers.</p>
<p>For Contact.app, rather than rendering “Hello World!” at the root
path, we are going to do something a little fancy: we are going to
redirect to another path, the <code>/contacts</code> path. Redirects are
a feature of HTTP that allow you to redirect a client to another
location with an HTTP response.</p>
<p>We are going to display a list of contacts as our root page, and,
arguably, redirecting to the <code>/contacts</code> path to display this
information is a bit more consistent with the notion of resources with
REST. This is a judgement call on our part, and not something we feel is
too important, but it makes sense in terms of routes we will set up
later in the application.</p>
<p>To change our “Hello World” route to a redirect, we only need to
change one line of code:</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/"</span>)</span>
<span id="cb2-2"><a aria-hidden="true" href="#cb2-2" tabindex="-1"></a><span class="kw">def</span> index():</span>
<span id="cb2-3"><a aria-hidden="true" href="#cb2-3" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts"</span>) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Changing “Hello World” to a redirect</p></figcaption>
</figure>
<ol>
<li><p>Update to a call to <code>redirect()</code></p></li>
</ol>
<p>Now the <code>index()</code> function returns the result of the
Flask-supplied <code>redirect()</code> function with the path we’ve
supplied. In this case the path is <code>/contacts</code>, passed in as
a string argument. Now, if you navigate to the root path,
<code>/</code>, our Flask application will forward you on to the
<code>/contacts</code> path.</p>
<h2 id="_contact_app_functionality">Contact.app Functionality</h2>
<p>Now that we have some understanding of how to define routes, let’s
get down to specifying and then implementing our web application.</p>
<p>What will Contact.app do?</p>
<p>Initially, it will allow users to:</p>
<ul>
<li><p>View a list of contacts, including first name, last name, phone
and email address</p></li>
<li><p>Search the contacts</p></li>
<li><p>Add a new contact</p></li>
<li><p>View the details of a contact</p></li>
<li><p>Edit the details of a contact</p></li>
<li><p>Delete a contact</p></li>
</ul>
<p>So, as you can see, Contact.app is a CRUD application, the sort of
application that is perfect for an old-school web 1.0 approach.</p>
<p>Note that the source code of Contact.app is available on <a href="https://github.com/bigskysoftware/contact-app">GitHub</a>.</p>
<h3 id="_showing_a_searchable_list_of_contacts">Showing A Searchable
List Of Contacts</h3>
<p>Let’s add our first real bit of functionality: the ability to show
all the contacts in our app in a list (really, in a table).</p>
<p>This functionality is going to be found at the <code>/contacts</code>
path, which is the path our previous route is redirecting to.</p>
<p>We will use Flask to route the <code>/contacts</code> path to a
handler function, <code>contacts()</code>. This function will do one of
two things:</p>
<ul>
<li><p>If there is a search term found in the request, it will filter
down to only contacts matching that term</p></li>
<li><p>If not, it will simply list all contacts</p></li>
</ul>
<p>This is a common approach in web 1.0 style applications: the same URL
that displays all instances of some resource also serves as the search
results page for those resources. Taking this approach makes it easy to
reuse the list display that is common to both types of request.</p>
<p>Here is what the code looks like for this handler:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb3-5"><a aria-hidden="true" href="#cb3-5" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb3-6"><a aria-hidden="true" href="#cb3-6" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb3-7"><a aria-hidden="true" href="#cb3-7" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>() <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb3-8"><a aria-hidden="true" href="#cb3-8" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>, contacts<span class="op">=</span>contacts_set)</span></code></pre></div>
<figcaption><p>A handler for server-side search</p></figcaption>
</figure>
<ol>
<li><p>Look for the query parameter named <code>q</code>, which stands
for “query.”</p></li>
<li><p>If the parameter exists, call the <code>Contact.search()</code>
function with it.</p></li>
<li><p>If not, call the <code>Contact.all()</code> function.</p></li>
<li><p>Pass the result to the <code>index.html</code> template to render
to the client.</p></li>
</ol>
<p>We see the same sort of routing code we saw in our first example, but
we have a more elaborate handler function. First, we check to see if a
search query parameter named <code>q</code> is part of the request.</p>
<dl>
<dt>Query Strings</dt>
<dd>
<p>A “query string” is part of the URL specification. Here is an example
URL with a query string in it:
<code>https://example.com/contacts?q=joe</code>. The query string is
everything after the <code>?</code>, and has a name-value pair format.
In this URL, the query parameter <code>q</code> is set to the string
value <code>joe</code>. In plain HTML, a query string can be included in
a request either by being hardcoded in an anchor tag or, more
dynamically, by using a form tag with a <code>GET</code> request.</p>
</dd>
</dl>
<p>To return to our Flask route, if a query parameter named
<code>q</code> is found, we call out to the <code>search()</code> method
on a <code>Contact</code> model object to do the actual contact search
and return all the matching contacts.</p>
<p>If the query parameter is <em class="test">not</em> found, we simply get all
contacts by invoking the <code>all()</code> method on the
<code>Contact</code> object.</p>
<p>Finally, we render a template, <code>index.html</code> that displays
the given contacts, passing in the results of whichever of these two
functions we end up calling.</p>
<div id="sidebar">
<div>
<div>
<p><strong>A Note On The Contact Class</strong></p>
</div>
<div>
<p>The <code>Contact</code> Python class we’re using is the “domain
model” or just “model” class for our application, providing the
“business logic” around the management of Contacts.</p>
<p>It could be working with a database (it isn’t) or a simple flat file
(it is), but we’re going to skip over the internal details of the model.
Think of it as a “normal” domain model class, with methods on it that
act in a “normal” manner.</p>
<p>We will treat <code>Contact</code> as a <em class="test">resource</em>, and focus
on how to effectively provide hypermedia representations of that
resource to clients.</p>
</div>
</div>
</div>
<h4 id="_the_list_search_templates">The list &amp; search templates</h4>
<p>Now that we have our handler logic written, we’ll create a template
to render HTML in our response to the client. At a high level, our HTML
response needs to have the following elements:</p>
<ul>
<li><p>A list of any matching or all contacts.</p></li>
<li><p>A search box where a user may type and submit search
terms.</p></li>
<li><p>A bit of surrounding “chrome”: a header and footer for the
website that will be the same regardless of the page you are
on.</p></li>
</ul>
<p>We are using the Jinja2 templating language, which has the following
features:</p>
<ul>
<li><p>We can use double-curly braces, <code>{{ }}</code>, to embed
expression values in the template.</p></li>
<li><p>we can use curly-percents, <code>{% %}</code>, for directives,
like iteration or including other content.</p></li>
</ul>
<p>Beyond this basic syntax, Jinja2 is very similar to other templating
languages used to generate content, and should be easy to follow for
most web developers.</p>
<p>Let’s look at the first few lines of code in the
<code>index.html</code> template:</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a>{% extends 'layout.html' %} <span class="er">&lt;</span>1&gt;</span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a></span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a>{% block content %} <span class="er">&lt;</span>2&gt;</span>
<span id="cb4-4"><a aria-hidden="true" href="#cb4-4" tabindex="-1"></a></span>
<span id="cb4-5"><a aria-hidden="true" href="#cb4-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"get"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"tool-bar"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>3&gt;</span>
<span id="cb4-6"><a aria-hidden="true" href="#cb4-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"search"</span><span class="dt">&gt;</span>Search Term<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb4-7"><a aria-hidden="true" href="#cb4-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb4-8"><a aria-hidden="true" href="#cb4-8" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span><span class="ot"> </span><span class="dt">/&gt;</span> <span class="er">&lt;</span>4&gt;</span>
<span id="cb4-9"><a aria-hidden="true" href="#cb4-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"submit"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"Search"</span><span class="dt">/&gt;</span></span>
<span id="cb4-10"><a aria-hidden="true" href="#cb4-10" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Start of index.html</p></figcaption>
</figure>
<ol>
<li><p>Set the layout template for this template.</p></li>
<li><p>Delimit the content to be inserted into the layout.</p></li>
<li><p>Create a search form that will issue an HTTP <code>GET</code> to
<code>/contacts</code>.</p></li>
<li><p>Create an input for a user to type search queries.</p></li>
</ol>
<p>The first line of code references a base template,
<code>layout.html</code>, with the <code>extends</code> directive. This
layout template provides the layout for the page (again, sometimes
called “the chrome”): it wraps the template content in an
<code>&lt;html&gt;</code> tag, imports any necessary CSS and JavaScript
in a <code>&lt;head&gt;</code> element, places a
<code>&lt;body&gt;</code> tag around the main content and so forth. All
the common content wrapped around the “normal” content for the entire
application is located in this file.</p>
<p>The next line of code declares the <code>content</code> section of
this template. This content block is used by the
<code>layout.html</code> template to inject the content of
<code>index.html</code> within its HTML.</p>
<p>Next we have our first bit of actual HTML, rather than just Jinja
directives. We have a simple HTML form that allows you to search
contacts by issuing a <code>GET</code> request to the
<code>/contacts</code> path. The form itself contains a label and an
input with the name “q.” This input’s value will be submitted with the
<code>GET</code> request to the <code>/contacts</code> path, as a query
string (since this is a <code>GET</code> request.)</p>
<p>Note that the value of this input is set to the Jinja expression
<code>{{ request.args.get('q') or '' }}</code>. This expression is
evaluated by Jinja and will insert the request value of “q” as the
input’s value, if it exists. This will “preserve” the search value when
a user does a search, so that when the results of a search are rendered
the text input contains the term that was searched for. This makes for a
better user experience since the user can see exactly what the current
results match, rather than having a blank text box at the top of the
screen.</p>
<p>Finally, we have a submit-type input. This will render as a button
and, when it is clicked, it will trigger the form to issue an HTTP
request.</p>
<p>This search interface forms the top of our contact page. Following it
is a table of contacts, either all contacts or the contacts that match
the search, if a search was done.</p>
<p>Here is what the template code for the contact table looks like:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">table</span><span class="dt">&gt;</span></span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>First <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Last <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Phone <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Email <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb5-6"><a aria-hidden="true" href="#cb5-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span id="cb5-7"><a aria-hidden="true" href="#cb5-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb5-8"><a aria-hidden="true" href="#cb5-8" tabindex="-1"></a> {% for contact in contacts %} <span class="er">&lt;</span>2&gt;</span>
<span id="cb5-9"><a aria-hidden="true" href="#cb5-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb5-10"><a aria-hidden="true" href="#cb5-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.first }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb5-11"><a aria-hidden="true" href="#cb5-11" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.last }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb5-12"><a aria-hidden="true" href="#cb5-12" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.phone }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb5-13"><a aria-hidden="true" href="#cb5-13" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.email }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span> <span class="er">&lt;</span>3&gt;</span>
<span id="cb5-14"><a aria-hidden="true" href="#cb5-14" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb5-15"><a aria-hidden="true" href="#cb5-15" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span> <span class="er">&lt;</span>4&gt;</span>
<span id="cb5-16"><a aria-hidden="true" href="#cb5-16" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb5-17"><a aria-hidden="true" href="#cb5-17" tabindex="-1"></a> {% endfor %}</span>
<span id="cb5-18"><a aria-hidden="true" href="#cb5-18" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb5-19"><a aria-hidden="true" href="#cb5-19" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The contacts table</p></figcaption>
</figure>
<ol>
<li><p>Output some headers for our table.</p></li>
<li><p>Iterate over the contacts that were passed in to the
template.</p></li>
<li><p>Output the values of the current contact, first name, last name,
etc.</p></li>
<li><p>An “operations” column, with links to edit or view the contact
details.</p></li>
</ol>
<p>This is the core of the page: we construct a table with appropriate
headers matching the data we are going to show for each contact. We
iterate over the contacts that were passed into the template by the
handler method using the <code>for</code> loop directive in Jinja2. We
then construct a series of rows, one for each contact, where we render
the first and last name, phone and email of the contact as table cells
in the row.</p>
<p>Additionally, we have a table cell that includes two links:</p>
<ul>
<li><p>A link to the “Edit” page for the contact, located at
<code>/contacts/{{ contact.id }}/edit</code> (e.g., For the contact with
id 42, the edit link will point to
<code>/contacts/42/edit</code>)</p></li>
<li><p>A link to the “View” page for the contact
<code>/contacts/{{ contact.id }}</code> (using our previous contact
example, the view page would be at <code>/contacts/42</code>)</p></li>
</ul>
<p>Finally, we have a bit of end-matter: a link to add a new contact and
a Jinja2 directive to end the <code>content</code> block:</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/new"</span><span class="dt">&gt;</span>Add Contact<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a></span>
<span id="cb6-5"><a aria-hidden="true" href="#cb6-5" tabindex="-1"></a>{% endblock %} <span class="er">&lt;</span>2&gt;</span></code></pre></div>
<figcaption><p>The “add contact” link</p></figcaption>
</figure>
<ol>
<li><p>Link to the page that allows you to create a new
contact.</p></li>
<li><p>The closing element of the <code>content</code> block.</p></li>
</ol>
<p>And that’s our complete template. Using this simple server-side
template, in combination with our handler method, we can respond with an
HTML <em class="test">representation</em> of all the contacts requested. So far, so
hypermedia.</p>
<p><a class="ref" href="#fig-contactapp">[fig-contactapp]</a> is what
the template looks like, rendered with a bit of contact information.</p>
<figure id="fig-contactapp">
<p><img src="https://hypermedia.systems/images/figure_2-2_table_etc.png"/></p>
<figcaption><p>Contact.app</p></figcaption>
</figure>
<p>Now, our application won’t win any design awards at this point, but
notice that our template, when rendered, provides all the functionality
necessary to see all the contacts and search them, and also provides
links to edit them, view details of them or even create a new one.</p>
<p>And it does all this without the client (that is, the browser)
knowing a thing about what contacts are or how to work with them.
Everything is encoded <em class="test">in</em> the hypermedia. A web browser
accessing this application just knows how to issue HTTP requests and
then render HTML, nothing more about the specifics of our applications
end points or underlying domain model.</p>
<p>As simple as our application is at this point, it is thoroughly
RESTful.</p>
<h3 id="_adding_a_new_contact">Adding A New Contact</h3>
<p>The next bit of functionality that we will add to our application is
the ability to add new contacts. To do this, we are going to need to
handle that <code>/contacts/new</code> URL referenced in the “Add
Contact” link above. Note that when a user clicks on that link, the
browser will issue a <code>GET</code> request to the
<code>/contacts/new</code> URL.</p>
<p>All the other routes we have so far use <code>GET</code> as well, but
we are actually going to use two different HTTP methods for this bit of
functionality: an HTTP <code>GET</code> to render a form for adding a
new contact, and then an HTTP <code>POST</code> <em class="test">to the same
path</em> to actually create the contact, so we are going to be explicit
about the HTTP method we want to handle when we declare this route.</p>
<p>Here is the code:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/new"</span>, methods<span class="op">=</span>[<span class="st">'GET'</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a><span class="kw">def</span> contacts_new_get():</span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"new.html"</span>, contact<span class="op">=</span>Contact()) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>The “new contact” GET route</p></figcaption>
</figure>
<ol>
<li><p>Declare a route, explicitly handling <code>GET</code> requests to
this path.</p></li>
<li><p>Render the <code>new.html</code> template, passing in a new
contact object.</p></li>
</ol>
<p>Simple enough. We just render a <code>new.html</code> template with a
new Contact. (<code>Contact()</code> is how you construct a new instance
of the <code>Contact</code> class in Python, if you aren’t familiar with
it.)</p>
<p>While the handler code for this route is very simple, the
<code>new.html</code> template is more complicated.</p>
<div id="sidebar">
<div>
<div>
<p>For the remaining templates we are going to omit the layout directive
and the content block declaration, but you can assume they are the same
unless we say otherwise. This will let us focus on the “meat” of the
template.</p>
</div>
</div>
</div>
<p>If you are familiar with HTML you are probably expecting a form
element here, and you will not be disappointed. We are going to use the
standard form hypermedia control for collecting contact information and
submitting it to the server.</p>
<p>Here is what our HTML looks like:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts/new"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"post"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">fieldset</span><span class="dt">&gt;</span></span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">legend</span><span class="dt">&gt;</span>Contact Values<span class="dt">&lt;/</span><span class="kw">legend</span><span class="dt">&gt;</span></span>
<span id="cb8-4"><a aria-hidden="true" href="#cb8-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb8-5"><a aria-hidden="true" href="#cb8-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb8-6"><a aria-hidden="true" href="#cb8-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span></span>
<span id="cb8-7"><a aria-hidden="true" href="#cb8-7" tabindex="-1"></a><span class="ot"> type</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Email"</span></span>
<span id="cb8-8"><a aria-hidden="true" href="#cb8-8" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.email or '' }}"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>3&gt;</span>
<span id="cb8-9"><a aria-hidden="true" href="#cb8-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span></span>
<span id="cb8-10"><a aria-hidden="true" href="#cb8-10" tabindex="-1"></a> {{ contact.errors['email'] }} <span class="er">&lt;</span>4&gt;</span>
<span id="cb8-11"><a aria-hidden="true" href="#cb8-11" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb8-12"><a aria-hidden="true" href="#cb8-12" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The “new contact” form</p></figcaption>
</figure>
<ol>
<li><p>A form that submits to the <code>/contacts/new</code> path, using
an HTTP <code>POST</code>.</p></li>
<li><p>A label for the first form input.</p></li>
<li><p>The first form input, of type email.</p></li>
<li><p>Any error messages associated with this field.</p></li>
</ol>
<p>In the first line of code we create a form that will submit back
<em class="test">to the same path</em> that we are handling:
<code>/contacts/new</code>. Rather than issuing an HTTP <code>GET</code>
to this path, however, we will issue an HTTP <code>POST</code> to it.
Using a <code>POST</code> in this manner will signal to the server that
we want to create a new Contact, rather than get a form for creating
one.</p>
<p>We then have a label (always a good practice!) and an input that
captures the email of the contact being created. The name of the input
is <code>email</code> and, when this form is submitted, the value of
this input will be submitted in the <code>POST</code> request,
associated with the <code>email</code> key.</p>
<p>Next we have inputs for the other fields for contacts:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"first_name"</span><span class="dt">&gt;</span>First Name<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"first_name"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"first_name"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span></span>
<span id="cb9-4"><a aria-hidden="true" href="#cb9-4" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"First Name"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.first or '' }}"</span><span class="dt">&gt;</span></span>
<span id="cb9-5"><a aria-hidden="true" href="#cb9-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['first'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb9-6"><a aria-hidden="true" href="#cb9-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb9-7"><a aria-hidden="true" href="#cb9-7" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb9-8"><a aria-hidden="true" href="#cb9-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"last_name"</span><span class="dt">&gt;</span>Last Name<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb9-9"><a aria-hidden="true" href="#cb9-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"last_name"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"last_name"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span></span>
<span id="cb9-10"><a aria-hidden="true" href="#cb9-10" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Last Name"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.last or '' }}"</span><span class="dt">&gt;</span></span>
<span id="cb9-11"><a aria-hidden="true" href="#cb9-11" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['last'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb9-12"><a aria-hidden="true" href="#cb9-12" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb9-13"><a aria-hidden="true" href="#cb9-13" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb9-14"><a aria-hidden="true" href="#cb9-14" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"phone"</span><span class="dt">&gt;</span>Phone<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb9-15"><a aria-hidden="true" href="#cb9-15" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"phone"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"phone"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Phone"</span></span>
<span id="cb9-16"><a aria-hidden="true" href="#cb9-16" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.phone or '' }}"</span><span class="dt">&gt;</span></span>
<span id="cb9-17"><a aria-hidden="true" href="#cb9-17" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['phone'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb9-18"><a aria-hidden="true" href="#cb9-18" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Inputs and labels for the “new contact”
form</p></figcaption>
</figure>
<p>Finally, we have a button that will submit the form, the end of the
form tag, and a link back to the main contacts table:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="dt">&gt;</span>Save<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb10-2"><a aria-hidden="true" href="#cb10-2" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">fieldset</span><span class="dt">&gt;</span></span>
<span id="cb10-3"><a aria-hidden="true" href="#cb10-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span>
<span id="cb10-4"><a aria-hidden="true" href="#cb10-4" tabindex="-1"></a></span>
<span id="cb10-5"><a aria-hidden="true" href="#cb10-5" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb10-6"><a aria-hidden="true" href="#cb10-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts"</span><span class="dt">&gt;</span>Back<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb10-7"><a aria-hidden="true" href="#cb10-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The submit button for the “new contact”
form</p></figcaption>
</figure>
<p>It is easy to miss in this straight-forward example: we are seeing
the flexibility of hypermedia in action.</p>
<p>If we add a new field, remove a field, or change the logic around how
fields are validated or work with one another, this new state of affairs
would be reflected in the new hypermedia representation given to users.
A user would see the updated new form and be able to work with these new
features, with no software update required.</p>
<h4 id="_handling_the_post_to_contactsnew">Handling the post to
/contacts/new</h4>
<p>The next step in our application is to handle the <code>POST</code>
that this form makes to <code>/contacts/new</code>.</p>
<p>To do so, we need to add another route to our application that
handles the <code>/contacts/new</code> path. The new route will handle
an HTTP <code>POST</code> method instead of an HTTP <code>GET</code>. We
will use the submitted form values to attempt to create a new
Contact.</p>
<p>If we are successful in creating a Contact, we will redirect the user
to the list of contacts and show a success message. If we aren’t
successful, then we will render the new contact form again with whatever
values the user entered and render error messages about what issues need
to be fixed so that the user can correct them.</p>
<p>Here is our new request handler:</p>
<figure>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/new"</span>, methods<span class="op">=</span>[<span class="st">'POST'</span>])</span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a><span class="kw">def</span> contacts_new():</span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> c <span class="op">=</span> Contact(</span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a> <span class="va">None</span>,</span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a> request.form[<span class="st">'first_name'</span>],</span>
<span id="cb11-6"><a aria-hidden="true" href="#cb11-6" tabindex="-1"></a> request.form[<span class="st">'last_name'</span>],</span>
<span id="cb11-7"><a aria-hidden="true" href="#cb11-7" tabindex="-1"></a> request.form[<span class="st">'phone'</span>],</span>
<span id="cb11-8"><a aria-hidden="true" href="#cb11-8" tabindex="-1"></a> request.form[<span class="st">'email'</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb11-9"><a aria-hidden="true" href="#cb11-9" tabindex="-1"></a> <span class="cf">if</span> c.save(): <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb11-10"><a aria-hidden="true" href="#cb11-10" tabindex="-1"></a> flash(<span class="st">"Created New Contact!"</span>)</span>
<span id="cb11-11"><a aria-hidden="true" href="#cb11-11" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts"</span>) <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb11-12"><a aria-hidden="true" href="#cb11-12" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb11-13"><a aria-hidden="true" href="#cb11-13" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"new.html"</span>, contact<span class="op">=</span>c) <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>The “new contact” controller code</p></figcaption>
</figure>
<ol>
<li><p>We construct a new contact object with the values from the
form.</p></li>
<li><p>We try to save it.</p></li>
<li><p>On success, “flash” a success message &amp; redirect to the
<code>/contacts</code> page.</p></li>
<li><p>On failure, re-render the form, showing any errors to the
user.</p></li>
</ol>
<p>The logic in this handler is a bit more complex than other methods we
have seen. The first thing we do is create a new Contact, again using
the <code>Contact()</code> syntax in Python to construct the object. We
pass in the values that the user submitted in the form by using the
<code>request.form</code> object, a feature provided by Flask.</p>
<p>This <code>request.form</code> allows us to access submitted form
values in an easy and convenient way, by simply passing in the same name
associated with the various inputs.</p>
<p>We also pass in <code>None</code> as the first value to the
<code>Contact</code> constructor. This is the “id” parameter, and by
passing in <code>None</code> we are signaling that it is a new contact,
and needs to have an ID generated for it. (Again, we are not going into
the details of how this model object is implemented, our only concern is
using it to generate hypermedia responses.)</p>
<p>Next, we call the <code>save()</code> method on the Contact object.
This method returns <code>true</code> if the save is successful, and
<code>false</code> if the save is unsuccessful (for example, a bad email
was submitted by the user).</p>
<p>If we are able to save the contact (that is, there were no validation
errors), we create a <em class="test">flash</em> message indicating success, and
redirect the browser back to the list page. A “flash” is a common
feature in web frameworks that allows you to store a message that will
be available on the <em class="test">next</em> request, typically in a cookie or in a
session store.</p>
<p>Finally, if we are unable to save the contact, we re-render the
<code>new.html</code> template with the contact. This will show the same
template as above, but the inputs will be filled in with the submitted
values, and any errors associated with the fields will be rendered to
feedback to the user as to what validation failed.</p>
<div id="sidebar">
<div>
<div>
<p><strong>The Post/Redirect/Get Pattern</strong></p>
</div>
<div>
<p>This handler implements a common strategy in web 1.0-style
development called the <a href="https://en.wikipedia.org/wiki/Post/Redirect/Get">Post/Redirect/Get</a>
or PRG pattern. By issuing an HTTP redirect once a contact has been
created and forwarding the browser on to another location, we ensure
that the <code>POST</code> does not end up in the browsers request
cache.</p>
<p>This means that if the user accidentally (or intentionally) refreshes
the page, the browser will not submit another <code>POST</code>,
potentially creating another contact. Instead, it will issue the
<code>GET</code> that we redirect to, which should be side-effect
free.</p>
<p>We will use the PRG pattern in a few different places in this
book.</p>
</div>
</div>
</div>
<p>OK, so we have our server-side logic set up to save contacts. And,
believe it or not, this is about as complicated as our handler logic
will get, even when we look at adding more sophisticated htmx-driven
behaviors.</p>
<h3 id="_viewing_the_details_of_a_contact">Viewing The Details Of A
Contact</h3>
<p>The next piece of functionality we will implement is the detail page
for a Contact. The user will navigate to this page by clicking the
“View” link in one of the rows in the list of contacts. This will take
them to the path <code>/contacts/&lt;contact id&gt;</code> (e.g.,
<code>/contacts/42</code>).</p>
<p>This is a common pattern in web development: contacts are treated as
resources and the URLs around these resources are organized in a
coherent manner.</p>
<ul>
<li><p>If you wish to view all contacts, you issue a <code>GET</code> to
<code>/contacts</code>.</p></li>
<li><p>If you want a hypermedia representation allowing you to create a
new contact, you issue a <code>GET</code> to
<code>/contacts/new</code>.</p></li>
<li><p>If you wish to view a specific contact (with, say, an id of
<code>42), you issue a `GET</code> to
<code>/contacts/42</code>.</p></li>
</ul>
<div id="sidebar">
<div>
<div>
<p><strong>The Eternal Bike Shed of URL Design</strong></p>
</div>
<div>
<p>It is easy to quibble about the particulars of the path scheme you
use for your application:</p>
<p>“Should we <code>POST</code> to <code>/contacts/new</code> or to
<code>/contacts</code>?”</p>
<p>We have seen many arguments online and in person advocating for one
approach versus another. We feel it is more important to understand the
overarching idea of <em class="test">resources</em> and <em class="test">hypermedia
representations</em>, rather than getting worked up about the smaller
details of your URL design.</p>
<p>We recommend you just pick a reasonable, resource-oriented URL layout
you like and then stay consistent. Remember, in a hypermedia system, you
can always change your endpoints later, because you are using hypermedia
as the engine of application state!</p>
</div>
</div>
</div>
<p>Our handler logic for the detail route is going to be <em class="test">very</em>
simple: we just look the Contact up by id, which is embedded in the path
of the URL for the route. To extract this ID we are going to need to
introduce a final bit of Flask functionality: the ability to call out
pieces of a path and have them automatically extracted and passed in to
a handler function.</p>
<p>Here is what the code looks like, just a few lines of simple
Python:</p>
<figure>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a aria-hidden="true" href="#cb12-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;"</span>) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb12-2"><a aria-hidden="true" href="#cb12-2" tabindex="-1"></a><span class="kw">def</span> contacts_view(contact_id<span class="op">=</span><span class="dv">0</span>): <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb12-3"><a aria-hidden="true" href="#cb12-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id) <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb12-4"><a aria-hidden="true" href="#cb12-4" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"show.html"</span>, contact<span class="op">=</span>contact) <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Map the path, with a path variable named
<code>contact_id</code>.</p></li>
<li><p>The handler takes the value of this path parameter.</p></li>
<li><p>Look up the corresponding contact.</p></li>
<li><p>Render the <code>show.html</code> template.</p></li>
</ol>
<p>You can see the syntax for extracting values from the path in the
first line of code: you enclose the part of the path you wish to extract
in <code>&lt;&gt;</code> and give it a name. This component of the path
will be extracted and then passed into the handler function, via the
parameter with the same name.</p>
<p>So, if you were to navigate to the path <code>/contacts/42</code>,
the value <code>42</code> would be passed into the
<code>contacts_view()</code> function for the value of
<code>contact_id</code>.</p>
<p>Once we have the id of the contact we want to look up, we load it up
using the <code>find</code> method on the <code>Contact</code> object.
We then pass this contact into the <code>show.html</code> template and
render a response.</p>
<h3 id="_the_contact_detail_template">The Contact Detail Template</h3>
<p>Our <code>show.html</code> template is relatively simple, just
showing the same information as the table but in a slightly different
format (perhaps for printing). If we add functionality like “notes” to
the application later on, this will give us a good place to do so.</p>
<p>Again, we will omit the “chrome” of the template and focus on the
meat:</p>
<figure>
<div class="sourceCode" id="cb13"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb13-1"><a aria-hidden="true" href="#cb13-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">h1</span><span class="dt">&gt;</span>{{contact.first}} {{contact.last}}<span class="dt">&lt;/</span><span class="kw">h1</span><span class="dt">&gt;</span></span>
<span id="cb13-2"><a aria-hidden="true" href="#cb13-2" tabindex="-1"></a></span>
<span id="cb13-3"><a aria-hidden="true" href="#cb13-3" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb13-4"><a aria-hidden="true" href="#cb13-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Phone: {{contact.phone}}<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb13-5"><a aria-hidden="true" href="#cb13-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span>Email: {{contact.email}}<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb13-6"><a aria-hidden="true" href="#cb13-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb13-7"><a aria-hidden="true" href="#cb13-7" tabindex="-1"></a></span>
<span id="cb13-8"><a aria-hidden="true" href="#cb13-8" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb13-9"><a aria-hidden="true" href="#cb13-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{contact.id}}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb13-10"><a aria-hidden="true" href="#cb13-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts"</span><span class="dt">&gt;</span>Back<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb13-11"><a aria-hidden="true" href="#cb13-11" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The “contact details” template</p></figcaption>
</figure>
<p>We simply render a First Name and Last Name header, with the
additional contact information below it, and a couple of links: a link
to edit the contact and a link to navigate back to the full list of
contacts.</p>
<h3 id="_editing_and_deleting_a_contact">Editing And Deleting A
Contact</h3>
<p>Next up we will tackle the functionality on the other end of that
“Edit” link. Editing a contact is going to look very similar to creating
a new contact. As with adding a new contact, we are going to need two
routes that handle the same path, but using different HTTP methods: a
<code>GET</code> to <code>/contacts/&lt;contact_id&gt;/edit</code> will
return a form allowing you to edit the contact and a <code>POST</code>
to that path will update it.</p>
<p>We are also going to piggyback the ability to delete a contact along
with this editing functionality. To do this we will need to handle a
<code>POST</code> to
<code>/contacts/&lt;contact_id&gt;/delete</code>.</p>
<p>Let’s look at the code to handle the <code>GET</code>, which, again,
will return an HTML representation of an editing interface for the given
resource:</p>
<figure>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a aria-hidden="true" href="#cb14-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;/edit"</span>, methods<span class="op">=</span>[<span class="st">"GET"</span>])</span>
<span id="cb14-2"><a aria-hidden="true" href="#cb14-2" tabindex="-1"></a><span class="kw">def</span> contacts_edit_get(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb14-3"><a aria-hidden="true" href="#cb14-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb14-4"><a aria-hidden="true" href="#cb14-4" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"edit.html"</span>, contact<span class="op">=</span>contact)</span></code></pre></div>
<figcaption><p>The “edit contact” controller code</p></figcaption>
</figure>
<p>As you can see this looks a lot like our “Show Contact”
functionality. In fact, it is nearly identical except for the template:
here we render <code>edit.html</code> rather than
<code>show.html</code>.</p>
<p>While our handler code looked similar to the “Show Contact”
functionality, the <code>edit.html</code> template is going to look very
similar to the template for the “New Contact” functionality: we will
have a form that submits updated contact values to the same “edit” URL
and that presents all the fields of a contact as inputs for editing,
along with any error messages.</p>
<p>Here is the first bit of the form:</p>
<figure>
<div class="sourceCode" id="cb15"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb15-1"><a aria-hidden="true" href="#cb15-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"post"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb15-2"><a aria-hidden="true" href="#cb15-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">fieldset</span><span class="dt">&gt;</span></span>
<span id="cb15-3"><a aria-hidden="true" href="#cb15-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">legend</span><span class="dt">&gt;</span>Contact Values<span class="dt">&lt;/</span><span class="kw">legend</span><span class="dt">&gt;</span></span>
<span id="cb15-4"><a aria-hidden="true" href="#cb15-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb15-5"><a aria-hidden="true" href="#cb15-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb15-6"><a aria-hidden="true" href="#cb15-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span></span>
<span id="cb15-7"><a aria-hidden="true" href="#cb15-7" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Email"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.email }}"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb15-8"><a aria-hidden="true" href="#cb15-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb15-9"><a aria-hidden="true" href="#cb15-9" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The “edit contact” form start</p></figcaption>
</figure>
<ol>
<li><p>Issue a <code>POST</code> to the
<code>/contacts/{{ contact.id }}/edit</code> path.</p></li>
<li><p>As with the <code>new.html</code> page, the input is tied to the
contact’s email.</p></li>
</ol>
<p>This HTML is nearly identical to our <code>new.html</code> form,
except that this form is going to submit a <code>POST</code> to a
different path, based on the id of the contact that we want to update.
(It’s worth mentioning here that, rather than <code>POST</code>, we
would prefer to use a <code>PUT</code> or <code>PATCH</code>, but those
are not available in plain HTML.)</p>
<p>Following this we have the remainder of our form, again very similar
to the <code>new.html</code> template, and our button to submit the
form.</p>
<figure>
<div class="sourceCode" id="cb16"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb16-1"><a aria-hidden="true" href="#cb16-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb16-2"><a aria-hidden="true" href="#cb16-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"first_name"</span><span class="dt">&gt;</span>First Name<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb16-3"><a aria-hidden="true" href="#cb16-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"first_name"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"first_name"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span></span>
<span id="cb16-4"><a aria-hidden="true" href="#cb16-4" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"First Name"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.first }}"</span><span class="dt">&gt;</span></span>
<span id="cb16-5"><a aria-hidden="true" href="#cb16-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['first'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb16-6"><a aria-hidden="true" href="#cb16-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb16-7"><a aria-hidden="true" href="#cb16-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb16-8"><a aria-hidden="true" href="#cb16-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"last_name"</span><span class="dt">&gt;</span>Last Name<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb16-9"><a aria-hidden="true" href="#cb16-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"last_name"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"last_name"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span></span>
<span id="cb16-10"><a aria-hidden="true" href="#cb16-10" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Last Name"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.last }}"</span><span class="dt">&gt;</span></span>
<span id="cb16-11"><a aria-hidden="true" href="#cb16-11" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['last'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb16-12"><a aria-hidden="true" href="#cb16-12" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb16-13"><a aria-hidden="true" href="#cb16-13" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb16-14"><a aria-hidden="true" href="#cb16-14" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"phone"</span><span class="dt">&gt;</span>Phone<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb16-15"><a aria-hidden="true" href="#cb16-15" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"phone"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"phone"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span></span>
<span id="cb16-16"><a aria-hidden="true" href="#cb16-16" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Phone"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.phone }}"</span><span class="dt">&gt;</span></span>
<span id="cb16-17"><a aria-hidden="true" href="#cb16-17" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['phone'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb16-18"><a aria-hidden="true" href="#cb16-18" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb16-19"><a aria-hidden="true" href="#cb16-19" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="dt">&gt;</span>Save<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb16-20"><a aria-hidden="true" href="#cb16-20" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">fieldset</span><span class="dt">&gt;</span></span>
<span id="cb16-21"><a aria-hidden="true" href="#cb16-21" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The “edit contact” form body</p></figcaption>
</figure>
<p>In the final part of our template we have a small difference between
the <code>new.html</code> and <code>edit.html</code>. Below the main
editing form, we include a second form that allows you to delete a
contact. It does this by issuing a <code>POST</code> to the
<code>/contacts/&lt;contact id&gt;/delete</code> path. Just as we would
prefer to use a <code>PUT</code> to update a contact, we would much
rather use an HTTP <code>DELETE</code> request to delete one.
Unfortunately that also isn’t possible in plain HTML.</p>
<p>To finish up the page, there is a simple hyperlink back to the list
of contacts.</p>
<figure>
<div class="sourceCode" id="cb17"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb17-1"><a aria-hidden="true" href="#cb17-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/delete"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"post"</span><span class="dt">&gt;</span></span>
<span id="cb17-2"><a aria-hidden="true" href="#cb17-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="dt">&gt;</span>Delete Contact<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb17-3"><a aria-hidden="true" href="#cb17-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span>
<span id="cb17-4"><a aria-hidden="true" href="#cb17-4" tabindex="-1"></a></span>
<span id="cb17-5"><a aria-hidden="true" href="#cb17-5" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb17-6"><a aria-hidden="true" href="#cb17-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/"</span><span class="dt">&gt;</span>Back<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb17-7"><a aria-hidden="true" href="#cb17-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The “edit contact” form footer</p></figcaption>
</figure>
<p>Given all the similarities between the <code>new.html</code> and
<code>edit.html</code> templates, you may be wondering why we are not
<em class="test">refactoring</em> these two templates to share logic between them.
That’s a good observation and, in a production system, we would probably
do just that.</p>
<p>For our purposes, however, since our application is small and simple,
we will leave the templates separate.</p>
<div id="sidebar">
<div>
<div>
<p><strong>Factoring Your Applications</strong></p>
</div>
<div>
<p>One thing that often trips people up who are coming to hypermedia
applications from a JavaScript background is the notion of “components”.
In JavaScript-oriented applications it is common to break your app up
into small client-side components that are then composed together. These
components are often developed and tested in isolation and provide a
nice abstraction for developers to create testable code.</p>
<p>With Hypermedia-Driven Applications, in contrast, you factor your
application on the server side. As we said, the above form could be
refactored into a shared template between the edit and create templates,
allowing you to achieve a reusable and DRY (Don’t Repeat Yourself)
implementation.</p>
<p>Note that factoring on the server-side tends to be coarser-grained
than on the client-side: you tend to split out common <em class="test">sections</em>
rather than create lots of individual components. This has benefits (it
tends to be simple) as well as drawbacks (it is not nearly as isolated
as client-side components).</p>
<p>Overall, a properly factored server-side hypermedia application can
be extremely DRY.</p>
</div>
</div>
</div>
<h4 id="_handling_the_post_to_contactscontact_id">Handling the post to
/contacts/&lt;contact_id&gt;/edit</h4>
<p>Next we need to handle the HTTP <code>POST</code> request that the
form in our <code>edit.html</code> template submits. We will declare
another route that handles the same path as the <code>GET</code>
above.</p>
<p>Here is the new handler code:</p>
<figure>
<div class="sourceCode" id="cb18"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb18-1"><a aria-hidden="true" href="#cb18-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;/edit"</span>, methods<span class="op">=</span>[<span class="st">"POST"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb18-2"><a aria-hidden="true" href="#cb18-2" tabindex="-1"></a><span class="kw">def</span> contacts_edit_post(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb18-3"><a aria-hidden="true" href="#cb18-3" tabindex="-1"></a> c <span class="op">=</span> Contact.find(contact_id) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb18-4"><a aria-hidden="true" href="#cb18-4" tabindex="-1"></a> c.update(</span>
<span id="cb18-5"><a aria-hidden="true" href="#cb18-5" tabindex="-1"></a> request.form[<span class="st">'first_name'</span>],</span>
<span id="cb18-6"><a aria-hidden="true" href="#cb18-6" tabindex="-1"></a> request.form[<span class="st">'last_name'</span>],</span>
<span id="cb18-7"><a aria-hidden="true" href="#cb18-7" tabindex="-1"></a> request.form[<span class="st">'phone'</span>],</span>
<span id="cb18-8"><a aria-hidden="true" href="#cb18-8" tabindex="-1"></a> request.form[<span class="st">'email'</span>]) <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb18-9"><a aria-hidden="true" href="#cb18-9" tabindex="-1"></a> <span class="cf">if</span> c.save(): <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span>
<span id="cb18-10"><a aria-hidden="true" href="#cb18-10" tabindex="-1"></a> flash(<span class="st">"Updated Contact!"</span>)</span>
<span id="cb18-11"><a aria-hidden="true" href="#cb18-11" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts/"</span> <span class="op">+</span> <span class="bu">str</span>(contact_id)) <span class="op">&lt;</span><span class="dv">5</span><span class="op">&gt;</span></span>
<span id="cb18-12"><a aria-hidden="true" href="#cb18-12" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb18-13"><a aria-hidden="true" href="#cb18-13" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"edit.html"</span>, contact<span class="op">=</span>c) <span class="op">&lt;</span><span class="dv">6</span><span class="op">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Handle a <code>POST</code> to
<code>/contacts/&lt;contact_id&gt;/edit</code>.</p></li>
<li><p>Look the contact up by id.</p></li>
<li><p>Update the contact with the new information from the
form.</p></li>
<li><p>Attempt to save it.</p></li>
<li><p>On success, flash a success message &amp; redirect to the detail
page.</p></li>
<li><p>On failure, re-render the edit template, showing any
errors.</p></li>
</ol>
<p>The logic in this handler is very similar to the logic in the handler
for adding a new contact. The only real difference is that, rather than
creating a new Contact, we look the contact up by id and then call the
<code>update()</code> method on it with the values that were entered in
the form.</p>
<p>Once again, this consistency between our CRUD operations is one of
the nice and simplifying aspects of traditional CRUD web
applications.</p>
<h3 id="_deleting_a_contact">Deleting A Contact</h3>
<p>We piggybacked contact delete functionality into the same template
used to edit a contact. This second form will issue an HTTP
<code>POST</code> to <code>/contacts/&lt;contact_id&gt;/delete</code>,
and we will need to create a handler for that path as well.</p>
<p>Here is what the controller looks like:</p>
<figure>
<div class="sourceCode" id="cb19"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb19-1"><a aria-hidden="true" href="#cb19-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;/delete"</span>, methods<span class="op">=</span>[<span class="st">"POST"</span>]) <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb19-2"><a aria-hidden="true" href="#cb19-2" tabindex="-1"></a><span class="kw">def</span> contacts_delete(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb19-3"><a aria-hidden="true" href="#cb19-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb19-4"><a aria-hidden="true" href="#cb19-4" tabindex="-1"></a> contact.delete() <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb19-5"><a aria-hidden="true" href="#cb19-5" tabindex="-1"></a> flash(<span class="st">"Deleted Contact!"</span>)</span>
<span id="cb19-6"><a aria-hidden="true" href="#cb19-6" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts"</span>) <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>The “delete contact” controller code</p></figcaption>
</figure>
<ol>
<li><p>Handle a <code>POST</code> the
<code>/contacts/&lt;contact_id&gt;/delete</code> path.</p></li>
<li><p>Look up and then invoke the <code>delete()</code> method on the
contact.</p></li>
<li><p>Flash a success message and redirect to the main list of
contacts.</p></li>
</ol>
<p>The handler code is very simple since we don’t need to do any
validation or conditional logic: we simply look up the contact the same
way we have been doing in our other handlers and invoke the
<code>delete()</code> method on it, then redirect back to the list of
contacts with a success flash message.</p>
<p>No need for a template in this case, the contact is gone.</p>
<h3 id="_contact_app_implemented">Contact.app…​ Implemented!</h3>
<p>And, well…​ believe it or not, that’s our entire contact
application!</p>
<p>If you’ve struggled with parts of the code so far, don’t worry: we
don’t expect you to be a Python or Flask expert (we aren’t!). You just
need a basic understanding of how they work to benefit from the
remainder of the book.</p>
<p>This is a small and simple application, but it does demonstrate many
of the aspects of traditional, web 1.0 applications: CRUD, the
Post/Redirect/Get pattern, working with domain logic in a controller,
organizing our URLs in a coherent, resource-oriented manner.</p>
<p>And, furthermore, this is a deeply <em class="test">Hypermedia-Driven</em> web
application. Without thinking about it very much, we have been using
REST, HATEOAS and all the other hypermedia concepts we discussed
earlier. We would bet that this simple little contact app of ours is
more RESTful than 99% of all JSON APIs ever built!</p>
<p>Just by virtue of using a <em class="test">hypermedia</em>, HTML, we naturally
fall into the RESTful network architecture.</p>
<p>So that’s great. But what’s the matter with this little web app? Why
not end here and go off to develop web 1.0 style applications?</p>
<p>Well, at some level, nothing is wrong with it. Particularly for an
application as simple as this one, the older way of building web apps
might be a perfectly acceptable approach.</p>
<p>However, our application does suffer from that “clunkiness” that we
mentioned earlier when discussing web 1.0 applications: every request
replaces the entire screen, introducing a noticeable flicker when
navigating between pages. You lose your scroll state. You have to click
around a bit more than you might in a more sophisticated web
application.</p>
<p>Contact.app, at this point, just doesn’t feel like a “modern” web
application.</p>
<p>Is it time to reach for a JavaScript framework and JSON APIs to make
our contact application more interactive?</p>
<p>No. No it isn’t.</p>
<p>It turns out that we can improve the user experience of this
application while retaining its fundamental hypermedia architecture.</p>
<p>In the next few chapters we will look at <a href="https://htmx.org">htmx</a>, a hypermedia-oriented library that
will let us improve our contact application while retaining the
hypermedia-based approach we have used so far.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Framework Soup</h2>
<p>Components encapsulate a section of a page along with its dynamic
behavior. While encapsulating behavior is a good way to organize code,
it can also separate elements from their surrounding context, which can
lead to wrong or inadequate relationships between elements. The result
is what one might call <em class="test">component soup</em>, where information is
hidden in component state, rather than being present in the HTML, which
is now incomprehensible due to missing context.</p>
<p>Before you reach for components for reuse, consider your options.
Lower-level mechanisms often (allow you to) produce better HTML. In some
cases, components can actually <em class="test">improve</em> the clarity of your
HTML.</p>
<blockquote>
<p>The fact that the HTML document is something that you barely touch,
because everything you need in there will be injected via JavaScript,
puts the document and the page structure out of focus.</p>
</blockquote><p class="quote-attribution"> Manuel Matuzović, <a href="https://www.matuzo.at/blog/2023/single-page-applications-criticism">Why
I’m not the biggest fan of Single Page Applications</a></p>
<p>In order to avoid <code>&lt;div&gt;</code> soup (or Markdown soup, or
Component soup), you need to be aware of the markup you’re producing and
be able to change it.</p>
<p>Some SPA frameworks, and some web components, make this more
difficult by putting layers of abstraction between the code the
developer writes and the generated markup.</p>
<p>While these abstractions can allow developers to create richer UI or
work faster, their pervasiveness means that developers can lose sight of
the actual HTML (and JavaScript) being sent to clients. Without diligent
testing, this leads to inaccessibility, poor SEO, and bloat.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Extending HTML As Hypermedia</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_a_close_look_at_a_hyperlink">A Close Look At A Hyperlink</a>
<ul>
<li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_why_only_anchors_forms">Why Only Anchors &amp; Forms?</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_why_only_click_submit_events">Why Only Click &amp; Submit
Events?</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_why_only_get_post">Why Only GET &amp; POST?</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_why_only_replace_the_entire_screen">Why Only Replace The Entire
Screen?</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_extending_html_as_a_hypermedia_with_htmx">Extending HTML as a
Hypermedia with Htmx</a>
<ul>
<li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_installing_and_using_htmx">Installing and Using Htmx</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_no_javascript_required">No JavaScript Required…​</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_triggering_http_requests">Triggering HTTP Requests</a>
<ul>
<li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_its_all_just_html">It’s All Just HTML</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_htmx_vs_plain_html_responses">Htmx vs. “Plain” HTML
Responses</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_targeting_other_elements">Targeting Other Elements</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_swap_styles">Swap Styles</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_using_events">Using Events</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_htmx_html_extended">Htmx: HTML eXtended</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_passing_request_parameters">Passing Request Parameters</a>
<ul>
<li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_enclosing_forms">Enclosing Forms</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_including_inputs">Including Inputs</a>
<ul>
<li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_relative_css_selectors">Relative CSS selectors</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_inline_values">Inline Values</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_history_support">History Support</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#_conclusion">Conclusion</a>
</li><li>
<a href="https://hypermedia.systems/extending-html-as-hypermedia/#html-note-title">HTML Notes: Budgeting For HTML</a>
</li></ul>
</details>
<div class="division-content">
<p>In the previous chapter we introduced a simple Web 1.0-style
hypermedia application to manage contacts. Our application supported the
normal CRUD operations for contacts, as well as a simple mechanism for
searching contacts. Our application was built using nothing but forms
and anchor tags, the traditional hypermedia controls used to interact
with servers. The application exchanges hypermedia (HTML) with the
server over HTTP, issuing <code>GET</code> and <code>POST</code> HTTP
requests and receiving back full HTML documents in response.</p>
<p>It is a basic web application, but it is also definitely a
Hypermedia-Driven Application. It is robust, it leverages the web’s
native technologies, and it is simple to understand.</p>
<p>So what’s not to like about the application?</p>
<p>Unfortunately, our application has a few issues common to web 1.0
style applications:</p>
<ul>
<li><p>From a user experience perspective: there is a noticeable refresh
when you move between pages of the application, or when you create,
update or delete a contact. This is because every user interaction (link
click or form submission) requires a full page refresh, with a whole new
HTML document to process after each action.</p></li>
<li><p>From a technical perspective, all the updates are done with the
<code>POST</code> HTTP method. This, despite the fact that more logical
actions and HTTP request types like <code>PUT</code> and
<code>DELETE</code> exist and would make more sense for some of the
operations we implemented. After all, if we wanted to delete a resource,
wouldn’t it make more sense to use an HTTP <code>DELETE</code> request
to do so? Somewhat ironically, since we have used pure HTML, we are
unable to access the full expressive power of HTTP, which was designed
specifically <em class="test">for</em> HTML.</p></li>
</ul>
<p>The first point, in particular, is noticeable in Web 1.0 style
applications like ours and is what is responsible for giving them the
reputation for being “clunky” when compared with their more
sophisticated JavaScript-based Single Page Application cousins.</p>
<p>We could address this issue by adopting a Single Page Application
framework, and updating our server-side to provide JSON-based responses.
Single Page Applications eliminate the clunkiness of web 1.0
applications by updating a web page without refreshing it: they can
mutate parts of the Document Object Model (DOM) of the existing page
without needing to replace (and re-render) the entire page.</p>
<div id="sidebar">
<div>
<div>
<p><strong>The DOM</strong></p>
</div>
<div>
<p>The DOM is the internal model that a browser builds up when it
processes HTML, forming a tree of “nodes” for the tags and other content
in the HTML. The DOM provides a programmatic JavaScript API that allows
you to update the nodes in a page directly, without the use of
hypermedia. Using this API, JavaScript code can insert new content, or
remove or update existing content, entirely outside the normal browser
request mechanism.</p>
</div>
</div>
</div>
<p>There are a few different styles of SPA, but, as we discussed in
Chapter 1, the most common approach today is to tie the DOM to a
JavaScript model and then let an SPA framework like <a href="https://reactjs.org/">React</a> or <a href="https://vuejs.org/">Vue</a> <em class="test">reactively</em> update the DOM
when a JavaScript model is updated: you make a change to a JavaScript
object that is stored locally in memory in the browser, and the web page
“magically” updates its state to reflect the change in the model.</p>
<p>In this style of application, communication with the server is
typically done via a JSON Data API, with the application sacrificing the
advantages of hypermedia in order to provide a better, smoother user
experience.</p>
<p>Many web developers today would not even consider the hypermedia
approach due to the perceived “legacy” feel of these web 1.0 style
applications.</p>
<p>Now, the second more technical issue we mentioned may strike you as a
bit pedantic, and we are the first to admit that conversations around
REST and which HTTP Action is right for a given operation can become
very tedious. But still, it’s odd that, when using plain HTML, it is
impossible to use all the functionality of HTTP!</p>
<p>Just seems wrong, doesn’t it?</p>
<h2 id="_a_close_look_at_a_hyperlink">A Close Look At A Hyperlink</h2>
<p>It turns out that we can boost the interactivity of our application
and address both of these issues <em class="test">without</em> resorting to the SPA
approach. We can do so by using a <em class="test">hypermedia-oriented</em>
JavaScript library, <a href="https://htmx.org">htmx</a>. The authors of
this book built htmx specifically to extend HTML as a hypermedia and
address the issues with legacy HTML applications we mentioned above (as
well as a few others.)</p>
<p>Before we get into how htmx allows us to improve the UX of our Web
1.0 style application, let’s revisit the hyperlink/anchor tag from
Chapter 1. Recall, a hyperlink is what is known as a <em class="test">hypermedia
control</em>, a mechanism that describes some sort of interaction with a
server by encoding information about that interaction directly and
completely within the control itself.</p>
<p>Consider again this simple anchor tag which, when interpreted by a
browser, creates a hyperlink to the website for this book:</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"https://hypermedia.systems/"</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a> Hypermedia Systems</span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A simple hyperlink, revisited</p></figcaption>
</figure>
<p>Let’s break down exactly what happens with this link:</p>
<ul>
<li><p>The browser will render the text “Hypermedia Systems” to the
screen, likely with a decoration indicating it is clickable.</p></li>
<li><p>Then, when a user clicks on the text…​</p></li>
<li><p>The browser will issue an HTTP <code>GET</code> to
<code>https://hypermedia.systems</code>…​</p></li>
<li><p>The browser will load the HTML body of the HTTP response into the
browser window, replacing the current document.</p></li>
</ul>
<p>So we have four aspects of a simple hypermedia link like this, with
the last three aspects supplying the mechanism that distinguishes a
hyperlink from “normal” text and, thus, makes this a hypermedia
control.</p>
<p>Now, let’s take a moment and think about how we can
<em class="test">generalize</em> these last three aspects of a hyperlink.</p>
<h3 id="_why_only_anchors_forms">Why Only Anchors &amp; Forms?</h3>
<p>Consider: what makes anchor tags (and forms) so special?</p>
<p>Why can’t other elements issue HTTP requests as well?</p>
<p>For example, why shouldn’t <code>button</code> elements be able to
issue HTTP requests? It seems arbitrary to have to wrap a form tag
around a button just to make deleting contacts work in our application,
for example.</p>
<p>Maybe: other elements should be able to issue HTTP requests as well.
Maybe other elements should be able to act as hypermedia controls on
their own.</p>
<p>This is our first opportunity to generalize HTML as a hypermedia.</p>
<div id="important">
<div>
<div>
<p><strong>Opportunity 1</strong></p>
</div>
<div>
<p>HTML could be extended to allow <em class="test">any</em> element to issue a
request to the server and act as a hypermedia control.</p>
</div>
</div>
</div>
<h3 id="_why_only_click_submit_events">Why Only Click &amp; Submit
Events?</h3>
<p>Next, let’s consider the event that triggers the request to the
server on our link: a click event.</p>
<p>Well, what’s so special about clicking (in the case of anchors) or
submitting (in the case of forms) things? Those are just two of many,
many events that are fired by the DOM, after all. Events like mouse
down, or key up, or blur are all events you might want to use to issue
an HTTP request.</p>
<p>Why shouldn’t these other events be able to trigger requests as
well?</p>
<p>This gives us our second opportunity to expand the expressiveness of
HTML:</p>
<div id="important">
<div>
<div>
<p><strong>Opportunity 2</strong></p>
</div>
<div>
<p>HTML could be extended to allow <em class="test">any</em> event — not just a
click, as in the case of hyperlinks — to trigger HTTP requests.</p>
</div>
</div>
</div>
<h3 id="_why_only_get_post">Why Only GET &amp; POST?</h3>
<p>Getting a bit more technical in our thinking leads us to the problem
we noted earlier: plain HTML only give us access to the <code>GET</code>
and <code>POST</code> actions of HTTP.</p>
<p>HTTP <em class="test">stands</em> for Hypertext Transfer Protocol, and yet the
format it was explicitly designed for, HTML, only supports two of the
five developer-facing request types. You <em class="test">have</em> to use JavaScript
and issue an AJAX request to get at the other three:
<code>DELETE</code>, <code>PUT</code> and <code>PATCH</code>.</p>
<p>Let’s recall what these different HTTP request types are designed to
represent:</p>
<ul>
<li><p><code>GET</code> corresponds with “getting” a representation for
a resource from a URL: it is a pure read, with no mutation of the
resource.</p></li>
<li><p><code>POST</code> submits an entity (or data) to the given
resource, often creating or mutating the resource and causing a state
change.</p></li>
<li><p><code>PUT</code> submits an entity (or data) to the given
resource for update or replacement, again likely causing a state
change.</p></li>
<li><p><code>PATCH</code> is similar to <code>PUT</code> but implies a
partial update and state change rather than a complete replacement of
the entity.</p></li>
<li><p><code>DELETE</code> deletes the given resource.</p></li>
</ul>
<p>These operations correspond closely to the CRUD operations we
discussed in Chapter 2. By giving us access to only two of the five,
HTML hamstrings our ability to take full advantage of HTTP.</p>
<p>This gives us our third opportunity to expand the expressiveness of
HTML:</p>
<div id="important">
<div>
<div>
<p><strong>Opportunity 3</strong></p>
</div>
<div>
<p>HTML could be extended so that it allows access to the missing three
HTTP methods, <code>PUT</code>, <code>PATCH</code> and
<code>DELETE</code>.</p>
</div>
</div>
</div>
<h3 id="_why_only_replace_the_entire_screen">Why Only Replace The Entire
Screen?</h3>
<p>As a final observation, consider the last aspect of a hyperlink: it
replaces the <em class="test">entire</em> screen when a user clicks on it.</p>
<p>It turns out that this technical detail is the primary culprit for
poor user experience in Web 1.0 Applications. A full page refresh can
cause a flash of unstyled content, where content “jumps” on the screen
as it transitions from its initial to its styled final form. It also
destroys the scroll state of the user by scrolling to the top of the
page, removes focus from a focused element and so forth.</p>
<p>But, if you think about it, there is no rule saying that hypermedia
exchanges <em class="test">must</em> replace the entire document.</p>
<p>This gives us our fourth, final and perhaps most important
opportunity to generalize HTML:</p>
<div id="important">
<div>
<div>
<p><strong>Opportunity 4</strong></p>
</div>
<div>
<p>HTML could be extended to allow the responses to requests to replace
elements <em class="test">within</em> the current document, rather than requiring
that they replace the <em class="test">entire</em> document.</p>
</div>
</div>
</div>
<p>This is actually a very old concept in hypermedia. Ted Nelson, in his
1980 book “Literary Machines” coined the term <em class="test">transclusion</em> to
capture this idea: the inclusion of content into an existing document
via a hypermedia reference. If HTML supported this style of “dynamic
transclusion,” then Hypermedia-Driven Applications could function much
more like a Single Page Application, where only part of the DOM is
updated by a given user interaction or network request.</p>
<h2 id="_extending_html_as_a_hypermedia_with_htmx">Extending HTML as a
Hypermedia with Htmx</h2>
<p>These four opportunities present us a way to extend HTML well beyond
its current abilities, but in a way that is <em class="test">entirely within</em> the
hypermedia model of the web. The fundamentals of HTML, HTTP, the
browser, and so on, won’t be changed dramatically. Rather, these
generalizations of <em class="test">existing functionality</em> already found within
HTML would simply let us accomplish <em class="test">more</em> using HTML.</p>
<p>Htmx is a JavaScript library that extends HTML in exactly this
manner, and it will be the focus of the next few chapters of this book.
Again, htmx is not the only JavaScript library that takes this
hypermedia-oriented approach (other excellent examples are <a href="https://unpoly.com">Unpoly</a> and <a href="https://hotwire.dev">Hotwire</a>), but htmx is the purest in its
pursuit of extending HTML as a hypermedia.</p>
<h3 id="_installing_and_using_htmx">Installing and Using Htmx</h3>
<p>From a practical “getting started” perspective, htmx is a simple,
dependency-free and stand-alone JavaScript library that can be added to
a web application by simply including it via a <code>script</code> tag
in your <code>head</code> element.</p>
<p>Because of this simple installation model, you can take advantage of
tools like public CDNs to install the library.</p>
<p>Below is an example using the popular <a href="https://unpkg.com">unpkg</a> Content Delivery Network (CDN) to
install version <code>1.9.2</code> of the library. We use an integrity
hash to ensure that the delivered JavaScript content matches what we
expect. This SHA can be found on the htmx website.</p>
<p>We also mark the script as <code>crossorigin="anonymous"</code> so no
credentials will be sent to the CDN.</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span id="cb2-2"><a aria-hidden="true" href="#cb2-2" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">script</span><span class="ot"> src</span><span class="op">=</span><span class="st">"https://unpkg.com/htmx.org@1.9.2"</span></span>
<span id="cb2-3"><a aria-hidden="true" href="#cb2-3" tabindex="-1"></a><span class="ot"> integrity</span><span class="op">=</span><span class="st">"sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/</span></span>
<span id="cb2-4"><a aria-hidden="true" href="#cb2-4" tabindex="-1"></a><span class="st"> bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h"</span></span>
<span id="cb2-5"><a aria-hidden="true" href="#cb2-5" tabindex="-1"></a><span class="ot"> crossorigin</span><span class="op">=</span><span class="st">"anonymous"</span><span class="dt">&gt;&lt;/</span><span class="kw">script</span><span class="dt">&gt;</span></span>
<span id="cb2-6"><a aria-hidden="true" href="#cb2-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">head</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Installing htmx</p></figcaption>
</figure>
<p>If you are used to modern JavaScript development, with complex build
systems and large numbers of dependencies, it may be a pleasant surprise
to find that that’s all it takes to install htmx.</p>
<p>This is in the spirit of the early web, when you could simply include
a script tag and things would “just work.”</p>
<p>If you don’t want to use a CDN, you can download htmx to your local
system and adjust the script tag to point to wherever you keep your
static assets. Or, you may have a build system that automatically
installs dependencies. In this case you can use the Node Package Manager
(npm) name for the library: <code>htmx.org</code> and install it in the
usual manner that your build system supports.</p>
<p>Once htmx has been installed, you can begin using it immediately.</p>
<h3 id="_no_javascript_required">No JavaScript Required…​</h3>
<p>And here we get to the interesting part of htmx: htmx does not
require you, the user of htmx, to actually write any JavaScript.</p>
<p>Instead, you will use <em class="test">attributes</em> placed directly on elements
in your HTML to drive more dynamic behavior. Htmx extends HTML as a
hypermedia, and it is designed to make that extension feel as natural
and consistent as possible with existing HTML concepts. Just as an
anchor tag uses an <code>href</code> attribute to specify the URL to
retrieve, and forms use an <code>action</code> attribute to specify the
URL to submit the form to, htmx uses HTML <em class="test">attributes</em> to specify
the URL that an HTTP request should be issued to.</p>
<h2 id="_triggering_http_requests">Triggering HTTP Requests</h2>
<p>Let’s look at the first feature of htmx: the ability for any element
in a web page to issue HTTP requests. This is the core functionality
provided by htmx, and it consists of five attributes that can be used to
issue the five different developer-facing types of HTTP requests:</p>
<ul>
<li><p><code>hx-get</code> - issues an HTTP <code>GET</code>
request.</p></li>
<li><p><code>hx-post</code> - issues an HTTP <code>POST</code>
request.</p></li>
<li><p><code>hx-put</code> - issues an HTTP <code>PUT</code>
request.</p></li>
<li><p><code>hx-patch</code> - issues an HTTP <code>PATCH</code>
request.</p></li>
<li><p><code>hx-delete</code> - issues an HTTP <code>DELETE</code>
request.</p></li>
</ul>
<p>Each of these attributes, when placed on an element, tells the htmx
library: “When a user clicks (or whatever) this element, issue an HTTP
request of the specified type.”</p>
<p>The values of these attributes are similar to the values of both
<code>href</code> on anchors and <code>action</code> on forms: you
specify the URL you wish to issue the given HTTP request type to.
Typically, this is done via a server-relative path.</p>
<p>For example, if we wanted a button to issue a <code>GET</code>
request to <code>/contacts</code> then we would write the following
HTML:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> Get The Contacts</span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A simple htmx-powered button</p></figcaption>
</figure>
<ol>
<li><p>A simple button that issues an HTTP <code>GET</code> to
<code>/contacts</code>.</p></li>
</ol>
<p>The htmx library will see the <code>hx-get</code> attribute on this
button, and hook up some JavaScript logic to issue an HTTP
<code>GET</code> AJAX request to the <code>/contacts</code> path when
the user clicks on it.</p>
<p>Very easy to understand and very consistent with the rest of
HTML.</p>
<h3 id="_its_all_just_html">It’s All Just HTML</h3>
<p>With the request issued by the button above, we get to perhaps the
most important thing to understand about htmx: it expects the response
to this AJAX request <em class="test">to be HTML</em>. Htmx is an extension of HTML.
A native hypermedia control like an anchor tag will typically get an
HTML response to an HTTP request it creates. Similarly, htmx expects the
server to respond to the requests that <em class="test">it</em> makes with HTML.</p>
<p>This may surprise web developers who are used to responding to an
AJAX request with JSON, which is far and away the most common response
format for such requests. But AJAX requests are just HTTP requests and
there is no rule saying they must use JSON. Recall again that AJAX
stands for Asynchronous JavaScript &amp; XML, so JSON is already a step
away from the format originally envisioned for this API: XML.</p>
<p>Htmx simply goes another direction and expects HTML.</p>
<h3 id="_htmx_vs_plain_html_responses">Htmx vs. “Plain” HTML
Responses</h3>
<p>There is an important difference between the HTTP responses to
“normal” anchor or form driven HTTP requests and to htmx-powered
requests: in the case of htmx triggered requests, responses can be
<em class="test">partial</em> bits of HTML.</p>
<p>In htmx-powered interactions, as you will see, we are often not
replacing the entire document. Rather we are using “transclusion” to
include content <em class="test">within</em> an existing document. Because of this,
it is often not necessary or desirable to transfer an entire HTML
document from the server to the browser.</p>
<p>This fact can be used to save bandwidth as well as resource loading
time. Less overall content is transferred from the server to the client,
and it isn’t necessary to reprocess a <code>head</code> tag with style
sheets, script tags, and so forth.</p>
<p>When the “Get Contacts” button is clicked, a <em class="test">partial</em> HTML
response might look something like this:</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">ul</span><span class="dt">&gt;</span></span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:joe@example.com"</span><span class="dt">&gt;</span>Joe<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:sarah@example.com"</span><span class="dt">&gt;</span>Sarah<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb4-4"><a aria-hidden="true" href="#cb4-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:fred@example.com"</span><span class="dt">&gt;</span>Fred<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb4-5"><a aria-hidden="true" href="#cb4-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">ul</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A partial HTML response to an htmx
request</p></figcaption>
</figure>
<p>This is just an unordered list of contacts with some clickable
elements in it. Note that there is no opening <code>html</code> tag, no
<code>head</code> tag, and so forth: it is a <em class="test">raw</em> HTML list,
without any decoration around it. A response in a real application might
contain more sophisticated HTML than this simple list, but even if it
were more complicated it wouldn’t need to be an entire page of HTML: it
could just be the “inner” content of the HTML representation for this
resource.</p>
<p>Now, this simple list response is perfect for htmx. Htmx will simply
take the returned content and then swap it in to the DOM in place of
some element in the page. (More on exactly where it will be placed in
the DOM in a moment.) Swapping in HTML content in this manner is fast
and efficient because it leverages the existing native HTML parser in
the browser, rather than requiring a significant amount of client-side
JavaScript to be executed.</p>
<p>This small HTML response shows how htmx stays within the hypermedia
paradigm: just like a “normal” hypermedia control in a “normal” web
application, we see hypermedia being transferred to the client in a
stateless and uniform manner.</p>
<p>This button just gives us a slightly more sophisticated mechanism for
building a web application using hypermedia.</p>
<h2 id="_targeting_other_elements">Targeting Other Elements</h2>
<p>Now, given that htmx has issued a request and gotten back some HTML
as a response, and that we are going to swap this content into the
existing page (rather than replacing the entire page), the question
becomes: where should this new content be placed?</p>
<p>It turns out that the default htmx behavior is to simply put the
returned content inside the element that triggered the request. That’s
<em class="test">not</em> a good thing in the case of our button: we will end up with
a list of contacts awkwardly embedded within the button element. That
will look pretty silly and is obviously not what we want.</p>
<p>Fortunately htmx provides another attribute, <code>hx-target</code>
which can be used to specify exactly <em class="test">where</em> in the DOM the new
content should be placed. The value of the <code>hx-target</code>
attribute is a Cascading Style Sheet (CSS) <em class="test">selector</em> that allows
you to specify the element to put the new hypermedia content into.</p>
<p>Let’s add a <code>div</code> tag that encloses the button with the id
<code>main</code>. We will then target this <code>div</code> with the
response:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a></span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> Get The Contacts</span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb5-6"><a aria-hidden="true" href="#cb5-6" tabindex="-1"></a></span>
<span id="cb5-7"><a aria-hidden="true" href="#cb5-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A simple htmx-powered button</p></figcaption>
</figure>
<ol>
<li><p>A <code>div</code> element that wraps the button.</p></li>
<li><p>The <code>hx-target</code> attribute that specifies the target of
the response.</p></li>
</ol>
<p>We have added <code>hx-target="#main"</code> to our button, where
<code>#main</code> is a CSS selector that says “The thing with the ID
‘main’.”</p>
<p>By using CSS selectors, htmx builds on top of familiar and standard
HTML concepts. This keeps the additional conceptual load for working
with htmx to a minimum.</p>
<p>Given this new configuration, what would the HTML on the client look
like after a user clicks on this button and a response has been received
and processed?</p>
<p>It would look something like this:</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span></span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">ul</span><span class="dt">&gt;</span></span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:joe@example.com"</span><span class="dt">&gt;</span>Joe<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:sarah@example.com"</span><span class="dt">&gt;</span>Sarah<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb6-5"><a aria-hidden="true" href="#cb6-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:fred@example.com"</span><span class="dt">&gt;</span>Fred<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb6-6"><a aria-hidden="true" href="#cb6-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">ul</span><span class="dt">&gt;</span></span>
<span id="cb6-7"><a aria-hidden="true" href="#cb6-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our HTML after the htmx request finishes</p></figcaption>
</figure>
<p>The response HTML has been swapped into the <code>div</code>,
replacing the button that triggered the request. Transclusion! And this
has happened “in the background” via AJAX, without a clunky page
refresh.</p>
<h2 id="_swap_styles">Swap Styles</h2>
<p>Now, perhaps we don’t want to load the content from the server
response <em class="test">into</em> the div, as child elements. Perhaps, for whatever
reason, we wish to <em class="test">replace</em> the entire div with the response. To
handle this, htmx provides another attribute, <code>hx-swap</code>, that
allows you to specify exactly <em class="test">how</em> the content should be swapped
into the DOM.</p>
<p>The <code>hx-swap</code> attribute supports the following values:</p>
<ul>
<li><p><code>innerHTML</code> - The default, replace the inner html of
the target element.</p></li>
<li><p><code>outerHTML</code> - Replace the entire target element with
the response.</p></li>
<li><p><code>beforebegin</code> - Insert the response before the target
element.</p></li>
<li><p><code>afterbegin</code> - Insert the response before the first
child of the target element.</p></li>
<li><p><code>beforeend</code> - Insert the response after the last child
of the target element.</p></li>
<li><p><code>afterend</code> - Insert the response after the target
element.</p></li>
<li><p><code>delete</code> - Deletes the target element regardless of
the response.</p></li>
<li><p><code>none</code> - No swap will be performed.</p></li>
</ul>
<p>The first two values, <code>innerHTML</code> and
<code>outerHTML</code>, are taken from the standard DOM properties that
allow you to replace content within an element or in place of an entire
element respectively.</p>
<p>The next four values are taken from the
<code>Element.insertAdjacentHTML()</code> DOM API, which allow you to
place an element or elements around a given element in various ways.</p>
<p>The last two values, <code>delete</code> and <code>none</code> are
specific to htmx. The first option will remove the target element from
the DOM, while the second option will do nothing (you may want to only
work with response headers, an advanced technique we will look at later
in the book.)</p>
<p>Again, you can see htmx stays as close as possible to existing web
standards in order to minimize the conceptual load necessary for its
use.</p>
<p>So let’s consider that case where, rather than replacing the
<code>innerHTML</code> content of the main div above, we want to replace
the <em class="test">entire div</em> with the HTML response.</p>
<p>To do so would require only a small change to our button, adding a
new <code>hx-swap</code> attribute:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span></span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a></span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a> Get The Contacts</span>
<span id="cb7-5"><a aria-hidden="true" href="#cb7-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb7-6"><a aria-hidden="true" href="#cb7-6" tabindex="-1"></a></span>
<span id="cb7-7"><a aria-hidden="true" href="#cb7-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Replacing the entire div</p></figcaption>
</figure>
<ol>
<li><p>The <code>hx-swap</code> attribute specifies how to swap in new
content.</p></li>
</ol>
<p>Now, when a response is received, the <em class="test">entire</em> div will be
replaced with the hypermedia content:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">ul</span><span class="dt">&gt;</span></span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:joe@example.com"</span><span class="dt">&gt;</span>Joe<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:sarah@example.com"</span><span class="dt">&gt;</span>Sarah<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb8-4"><a aria-hidden="true" href="#cb8-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"mailto:fred@example.com"</span><span class="dt">&gt;</span>Fred<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb8-5"><a aria-hidden="true" href="#cb8-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">ul</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our HTML after the htmx request finishes</p></figcaption>
</figure>
<p>You can see that, with this change, the target div has been entirely
removed from the DOM, and the list that was returned as the response has
replaced it.</p>
<p>Later in the book we will see additional uses for
<code>hx-swap</code>, for example when we implement infinite scrolling
in our contact management application.</p>
<p>Note that with the <code>hx-get</code>, <code>hx-post</code>,
<code>hx-put</code>, <code>hx-patch</code> and <code>hx-delete</code>
attributes, we have addressed two of the four opportunities for
improvement that we enumerated regarding plain HTML:</p>
<ul>
<li><p>Opportunity 1: We can now issue an HTTP request with <em class="test">any</em>
element (in this case we are using a button).</p></li>
<li><p>Opportunity 3: We can issue <em class="test">any sort</em> of HTTP request we
want, <code>PUT</code>, <code>PATCH</code> and <code>DELETE</code>, in
particular.</p></li>
</ul>
<p>And, with <code>hx-target</code> and <code>hx-swap</code> we have
addressed a third shortcoming: the requirement that the entire page be
replaced.</p>
<ul>
<li><p>Opportunity 4: We can now replace any element we want in our page
via transclusion, and we can do so in any manner we want.</p></li>
</ul>
<p>So, with only seven relatively simple additional attributes, we have
addressed most of the shortcomings of HTML as a hypermedia that we
identified earlier.</p>
<p>What’s next? Recall the one other opportunity we noted: the fact that
only a <code>click</code> event (on an anchor) or a <code>submit</code>
event (on a form) can trigger an HTTP request. Let’s look at how we can
address that limitation.</p>
<h2 id="_using_events">Using Events</h2>
<p>Thus far we have been using a button to issue a request with htmx.
You have probably intuitively understood that the button would issue its
request when you clicked on the button since, well, that’s what you do
with buttons: you click on them.</p>
<p>And, yes, by default when an <code>hx-get</code> or another
request-driving annotation from htmx is placed on a button, the request
will be issued when the button is clicked.</p>
<p>However, htmx generalizes this notion of an event triggering a
request by using, you guessed it, another attribute:
<code>hx-trigger</code>. The <code>hx-trigger</code> attribute allows
you to specify one or more events that will cause the element to trigger
an HTTP request.</p>
<p>Often you don’t need to use <code>hx-trigger</code> because the
default triggering event will be what you want. The default triggering
event depends on the element type, and should be fairly intuitive:</p>
<ul>
<li><p>Requests on <code>input</code>, <code>textarea</code> &amp;
<code>select</code> elements are triggered by the <code>change</code>
event.</p></li>
<li><p>Requests on <code>form</code> elements are triggered on the
<code>submit</code> event.</p></li>
<li><p>Requests on all other elements are triggered by the
<code>click</code> event.</p></li>
</ul>
<p>To demonstrate how <code>hx-trigger</code> works, consider the
following situation: we want to trigger the request on our button when
the mouse enters it. Now, this is certainly not a <em class="test">good</em> UX
pattern, but bear with us: we are just using this as an example.</p>
<p>To respond to a mouse entering the button, we would add the following
attribute to our button:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span></span>
<span id="cb9-4"><a aria-hidden="true" href="#cb9-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"mouseenter"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb9-5"><a aria-hidden="true" href="#cb9-5" tabindex="-1"></a> Get The Contacts</span>
<span id="cb9-6"><a aria-hidden="true" href="#cb9-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb9-7"><a aria-hidden="true" href="#cb9-7" tabindex="-1"></a></span>
<span id="cb9-8"><a aria-hidden="true" href="#cb9-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A (bad?) button that triggers on mouse
entry</p></figcaption>
</figure>
<ol>
<li><p>Issue a request on the… <code>mouseenter</code> event.</p></li>
</ol>
<p>Now, with this <code>hx-trigger</code> attribute in place, whenever
the mouse enters this button, a request will be triggered. Silly, but it
works.</p>
<p>Let’s try something a bit more realistic and potentially useful:
let’s add support for a keyboard shortcut for loading the contacts,
<code>Ctrl-L</code> (for “Load”). To do this we will need to take
advantage of additional syntax that the <code>hx-trigger</code>
attribute supports: event filters and additional arguments.</p>
<p>Event filters are a mechanism for determining if a given event should
trigger a request or not. They are applied to an event by adding square
brackets after it: <code>someEvent[someFilter]</code>. The filter itself
is a JavaScript expression that will be evaluated when the given event
occurs. If the result is truthy, in the JavaScript sense, it will
trigger the request. If not, the request will not be triggered.</p>
<p>In the case of keyboard shortcuts, we want to catch the
<code>keyup</code> event in addition to the click event:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span></span>
<span id="cb10-2"><a aria-hidden="true" href="#cb10-2" tabindex="-1"></a></span>
<span id="cb10-3"><a aria-hidden="true" href="#cb10-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span></span>
<span id="cb10-4"><a aria-hidden="true" href="#cb10-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"click, keyup"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb10-5"><a aria-hidden="true" href="#cb10-5" tabindex="-1"></a> Get The Contacts</span>
<span id="cb10-6"><a aria-hidden="true" href="#cb10-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb10-7"><a aria-hidden="true" href="#cb10-7" tabindex="-1"></a></span>
<span id="cb10-8"><a aria-hidden="true" href="#cb10-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A start, trigger on keyup</p></figcaption>
</figure>
<ol>
<li><p>A trigger with two events.</p></li>
</ol>
<p>Note that we have a comma separated list of events that can trigger
this element, allowing us to respond to more than one potential
triggering event. We still want to respond to the <code>click</code>
event and load the contacts, in addition to handling the
<code>Ctrl-L</code> keyboard shortcut.</p>
<p>Unfortunately there are two problems with our <code>keyup</code>
addition: As it stands, it will trigger requests on <em class="test">any</em> keyup
event that occurs. And, worse, it will only trigger when a keyup occurs
<em class="test">within</em> this button. The user would need to tab onto the button
to make it active and then begin typing.</p>
<p>Let’s fix these two issues. To fix the first one, we will use a
trigger filter to test that Control key and the “L” key are pressed
together:</p>
<figure>
<div class="sourceCode" id="cb11"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span></span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a></span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span></span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"click, keyup[ctrlKey </span><span class="er">&amp;&amp;</span><span class="st"> key == 'l']"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a> Get The Contacts</span>
<span id="cb11-6"><a aria-hidden="true" href="#cb11-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb11-7"><a aria-hidden="true" href="#cb11-7" tabindex="-1"></a></span>
<span id="cb11-8"><a aria-hidden="true" href="#cb11-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Getting better with filter on keyup</p></figcaption>
</figure>
<ol>
<li><p><code>keyup</code> now has a filter, so the control key and L
must be pressed.</p></li>
</ol>
<p>The trigger filter in this case is
<code>ctrlKey &amp;&amp; key == 'l'</code>. This can be read as “A key
up event, where the ctrlKey property is true and the key property is
equal to l.” Note that the properties <code>ctrlKey</code> and
<code>key</code> are resolved against the event rather than the global
name space, so you can easily filter on the properties of a given event.
You can use any expression you like for a filter, however: calling a
global JavaScript function, for example, is perfectly acceptable.</p>
<p>OK, so this filter limits the keyup events that will trigger the
request to only <code>Ctrl-L</code> presses. However, we still have the
problem that, as it stands, only <code>keyup</code> events
<em class="test">within</em> the button will trigger the request.</p>
<p>If you are not familiar with the JavaScript event bubbling model:
events typically “bubble” up to parent elements. So an event like
<code>keyup</code> will be triggered first on the focused element, and
then on its parent (enclosing) element, and so on, until it reaches the
top level <code>document</code> object that is the root of all other
elements.</p>
<p>To support a global keyboard shortcut that works regardless of what
element has focus, we will take advantage of event bubbling and a
feature that the <code>hx-trigger</code> attribute supports: the ability
to listen to <em class="test">other elements</em> for events. The syntax for doing
this is the <code>from:</code> modifier, which is added after an event
name and that allows you to specify a specific element to listen for the
given event on using a CSS selector.</p>
<p>In this case, we want to listen to the <code>body</code> element,
which is the parent element of all visible elements on the page.</p>
<p>Here is what our updated <code>hx-trigger</code> attribute looks
like:</p>
<figure>
<div class="sourceCode" id="cb12"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb12-1"><a aria-hidden="true" href="#cb12-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span></span>
<span id="cb12-2"><a aria-hidden="true" href="#cb12-2" tabindex="-1"></a></span>
<span id="cb12-3"><a aria-hidden="true" href="#cb12-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span></span>
<span id="cb12-4"><a aria-hidden="true" href="#cb12-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"click, keyup[ctrlKey </span><span class="er">&amp;&amp;</span><span class="st"> key == 'l'] from:body"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb12-5"><a aria-hidden="true" href="#cb12-5" tabindex="-1"></a> Get The Contacts</span>
<span id="cb12-6"><a aria-hidden="true" href="#cb12-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb12-7"><a aria-hidden="true" href="#cb12-7" tabindex="-1"></a></span>
<span id="cb12-8"><a aria-hidden="true" href="#cb12-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Even better, listen for keyup on the
body</p></figcaption>
</figure>
<ol>
<li><p>Listen to the ‘keyup” event on the <code>body</code>
tag.</p></li>
</ol>
<p>Now, in addition to clicks, the button will listen for
<code>keyup</code> events on the body of the page. So it will issue a
request when it is clicked on and also whenever someone hits
<code>Ctrl-L</code> within the body of the page.</p>
<p>And now we have a nice keyboard shortcut for our Hypermedia-Driven
Application.</p>
<p>The <code>hx-trigger</code> attribute supports many more modifiers,
and it is more elaborate than other htmx attributes. This is because
events, in general, are complicated and require a lot of details to get
just right. The default trigger will often suffice, however, and you
typically don’t need to reach for complicated <code>hx-trigger</code>
features when using htmx.</p>
<p>Even with more sophisticated trigger specifications like the keyboard
shortcut we just added, the overall feel of htmx is <em class="test">declarative</em>
rather than <em class="test">imperative</em>. That keeps htmx-powered applications
“feeling like” standard web 1.0 applications in a way that adding
significant amounts of JavaScript does not.</p>
<h2 id="_htmx_html_extended">Htmx: HTML eXtended</h2>
<p>And hey, check it out! With <code>hx-trigger</code> we have addressed
the final opportunity for improvement of HTML that we outlined at the
start of this chapter:</p>
<ul>
<li><p>Opportunity 2: We can use <em class="test">any</em> event to trigger an HTTP
request.</p></li>
</ul>
<p>That’s a grand total of eight, count ‘em, <em class="test">eight</em> attributes
that all fall squarely within the same conceptual model as normal HTML
and that, by extending HTML as a hypermedia, open up a whole new world
of user interaction possibilities within it.</p>
<p>Here is a table summarizing those opportunities and which htmx
attributes address them:</p>
<dl>
<dt>Any element should be able to make HTTP requests</dt>
<dd>
<p><code>hx-get</code>, <code>hx-post</code>, <code>hx-put</code>,
<code>hx-patch</code>, <code>hx-delete</code></p>
</dd>
<dt>Any event should be able to trigger an HTTP request</dt>
<dd>
<p><code>hx-trigger</code></p>
</dd>
<dt>Any HTTP Action should be available</dt>
<dd>
<p><code>hx-put</code>, <code>hx-patch</code>,
<code>hx-delete</code></p>
</dd>
<dt>Any place on the page should be replaceable (transclusion)</dt>
<dd>
<p><code>hx-target</code>, <code>hx-swap</code></p>
</dd>
</dl>
<h2 id="_passing_request_parameters">Passing Request Parameters</h2>
<p>So far we have just looked at a situation where a button makes a
simple <code>GET</code> request. This is conceptually very close to what
an anchor tag might do. But there is that other native hypermedia
control in HTML-based applications: forms. Forms are used to pass
additional information beyond just a URL up to the server in a
request.</p>
<p>This information is captured via input and input-like elements within
the form via the various types of input tags available in HTML.</p>
<p>Htmx allows you include this additional information in a way that
mirrors HTML itself.</p>
<h3 id="_enclosing_forms">Enclosing Forms</h3>
<p>The simplest way to pass input values with a request in htmx is to
enclose the element making a request within a form tag.</p>
<p>Let’s take our original search form and convert it to use htmx
instead:</p>
<figure>
<div class="sourceCode" id="cb13"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb13-1"><a aria-hidden="true" href="#cb13-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"get"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"tool-bar"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb13-2"><a aria-hidden="true" href="#cb13-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"search"</span><span class="dt">&gt;</span>Search Term<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb13-3"><a aria-hidden="true" href="#cb13-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span><span class="ot"> </span></span>
<span id="cb13-4"><a aria-hidden="true" href="#cb13-4" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span></span>
<span id="cb13-5"><a aria-hidden="true" href="#cb13-5" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Search Contacts"</span><span class="dt">/&gt;</span></span>
<span id="cb13-6"><a aria-hidden="true" href="#cb13-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb13-7"><a aria-hidden="true" href="#cb13-7" tabindex="-1"></a> Search</span>
<span id="cb13-8"><a aria-hidden="true" href="#cb13-8" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb13-9"><a aria-hidden="true" href="#cb13-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>An htmx-powered search button</p></figcaption>
</figure>
<ol>
<li><p>When an htmx-powered element is within an ancestor form tag, all
input values within that form will be submitted for non-<code>GET</code>
requests</p></li>
<li><p>We have switched from an <code>input</code> of type
<code>submit</code> to a <code>button</code> and added the
<code>hx-post</code> attribute</p></li>
</ol>
<p>Now, when a user clicks on this button, the value of the input with
the id <code>search</code> will be included in the request. This is by
virtue of the fact that there is a form tag enclosing both the button
and the input: when an htmx-driven request is triggered, htmx will look
up the DOM hierarchy for an enclosing form, and, if one is found, it
will include all values from within that form. (This is sometimes
referred to as “serializing” the form.)</p>
<p>You might have noticed that the button was switched from a
<code>GET</code> request to a <code>POST</code> request. This is
because, by default, htmx does <em class="test">not</em> include the closest
enclosing form for <code>GET</code> requests, but it <em class="test">does</em>
include the form for all other types of requests.</p>
<p>This may seem a little strange, but it avoids junking up URLs that
are used within forms when dealing with history entries, which we will
discuss in a bit. You can always include an enclosing form’s values with
an element that uses a <code>GET</code> by using the
<code>hx-include</code> attribute, which we will discuss next.</p>
<p>Note also that we could have added the <code>hx-post</code> attribute
to the form, rather than to the button but that would create a somewhat
awkward duplication of the search URL in the <code>action</code> and
<code>hx-post</code> attributes. This can be avoided by using the
<code>hx-boost</code> attribute, which we discuss in the next
chapter.</p>
<h3 id="_including_inputs">Including Inputs</h3>
<p>While enclosing all the inputs you want included in a request within
a form is the most common approach for serializing inputs for htmx
requests, it isn’t always possible or desirable: form tags can have
layout consequences and simply cannot be placed in some spots in HTML
documents. A good example of the latter situation is in table row
(<code>tr</code>) elements: the <code>form</code> tag is not a valid
child or parent of table rows, so you can’t place a form within or
around a row of data in a table.</p>
<p>To address this issue, htmx provides a mechanism for including input
values in requests: the <code>hx-include</code> attribute. The
<code>hx-include</code> attribute allows you to select input values that
you wish to include in a request via CSS selectors.</p>
<p>Here is the above example reworked to include the input, dropping the
form:</p>
<figure>
<div class="sourceCode" id="cb14"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb14-1"><a aria-hidden="true" href="#cb14-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"main"</span><span class="dt">&gt;</span></span>
<span id="cb14-2"><a aria-hidden="true" href="#cb14-2" tabindex="-1"></a></span>
<span id="cb14-3"><a aria-hidden="true" href="#cb14-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"search"</span><span class="dt">&gt;</span>Search Contacts:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb14-4"><a aria-hidden="true" href="#cb14-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> </span></span>
<span id="cb14-5"><a aria-hidden="true" href="#cb14-5" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span></span>
<span id="cb14-6"><a aria-hidden="true" href="#cb14-6" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Search Contacts"</span><span class="dt">/&gt;</span></span>
<span id="cb14-7"><a aria-hidden="true" href="#cb14-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="ot"> hx-include</span><span class="op">=</span><span class="st">"#search"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb14-8"><a aria-hidden="true" href="#cb14-8" tabindex="-1"></a> Search</span>
<span id="cb14-9"><a aria-hidden="true" href="#cb14-9" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb14-10"><a aria-hidden="true" href="#cb14-10" tabindex="-1"></a></span>
<span id="cb14-11"><a aria-hidden="true" href="#cb14-11" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>An htmx-powered search button with
<code>hx-include</code></p></figcaption>
</figure>
<ol>
<li><p><code>hx-include</code> can be used to include values directly in
a request.</p></li>
</ol>
<p>The <code>hx-include</code> attribute takes a CSS selector value and
allows you to specify exactly which values to send along with the
request. This can be useful if it is difficult to colocate an element
issuing a request with all the desired inputs.</p>
<p>It is also useful when you do, in fact, want to submit values with a
<code>GET</code> request and overcome the default behavior of htmx.</p>
<h4 id="_relative_css_selectors">Relative CSS selectors</h4>
<p>The <code>hx-include</code> attribute and, in fact, most attributes
that take a CSS selector, also support <em class="test">relative</em> CSS selectors.
These allow you to specify a CSS selector <em class="test">relative</em> to the
element it is declared on. Here are some examples:</p>
<dl>
<dt><code>closest</code></dt>
<dd>
<p>Find the closest parent element matching the given selector, e.g.,
<code>closest form</code>.</p>
</dd>
<dt><code>next</code></dt>
<dd>
<p>Find the next element (scanning forward) matching the given selector,
e.g., <code>next input</code>.</p>
</dd>
<dt><code>previous</code></dt>
<dd>
<p>Find the previous element (scanning backwards) matching the given
selector, e.g., <code>previous input</code>.</p>
</dd>
<dt><code>find</code></dt>
<dd>
<p>Find the next element within this element matching the given
selector, e.g., <code>find input</code>.</p>
</dd>
<dt><code>this</code></dt>
<dd>
<p>The current element.</p>
</dd>
</dl>
<p>Using relative CSS selectors often allows you to avoid generating ids
for elements, since you can take advantage of their local structural
layout instead.</p>
<h3 id="_inline_values">Inline Values</h3>
<p>A final way to include values in htmx-driven requests is to use the
<code>hx-vals</code> attribute, which allows you to include “static”
values in the request. This can be useful if you have additional
information that you want to include in requests, but you don’t want to
have this information embedded in, for example, hidden inputs (which
would be the standard mechanism for including additional, hidden
information in HTML.)</p>
<p>Here is an example of <code>hx-vals</code>:</p>
<figure>
<div class="sourceCode" id="cb15"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb15-1"><a aria-hidden="true" href="#cb15-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-vals</span><span class="op">=</span><span class="st">'{"state":"MT"}'</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb15-2"><a aria-hidden="true" href="#cb15-2" tabindex="-1"></a> Get The Contacts In Montana</span>
<span id="cb15-3"><a aria-hidden="true" href="#cb15-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>An htmx-powered button with
<code>hx-vals</code></p></figcaption>
</figure>
<ol>
<li><p><code>hx-vals</code>, a JSON value to include in the
request.</p></li>
</ol>
<p>The parameter <code>state</code> with the value <code>MT</code> will
be included in the <code>GET</code> request, resulting in a path and
parameters that looks like this: <code>/contacts?state=MT</code>. Note
that we switched the <code>hx-vals</code> attribute to use single quotes
around its value. This is because JSON strictly requires double quotes
and, therefore, to avoid escaping we needed to use the single-quote form
for the attribute value.</p>
<p>You can also prefix <code>hx-vals</code> with a <code>js:</code> and
pass values evaluated at the time of the request, which can be useful
for including things like a dynamically maintained variable, or value
from a third party JavaScript library.</p>
<p>For example, if the <code>state</code> variable were maintained
dynamically, via some JavaScript, and there existed a JavaScript
function, <code>getCurrentState()</code>, that returned the currently
selected state, it could be included dynamically in htmx requests like
so:</p>
<figure>
<div class="sourceCode" id="cb16"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb16-1"><a aria-hidden="true" href="#cb16-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb16-2"><a aria-hidden="true" href="#cb16-2" tabindex="-1"></a><span class="ot"> hx-vals</span><span class="op">=</span><span class="st">'js:{"state":getCurrentState()}'</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb16-3"><a aria-hidden="true" href="#cb16-3" tabindex="-1"></a> Get The Contacts In The Selected State</span>
<span id="cb16-4"><a aria-hidden="true" href="#cb16-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A dynamic value</p></figcaption>
</figure>
<ol>
<li><p>With the <code>js:</code> prefix, this expression will evaluate
at submit time.</p></li>
</ol>
<p>These three mechanisms, using <code>form</code> tags, using the
<code>hx-include</code> attribute and using the <code>hx-vals</code>
attribute, allow you to include values in your hypermedia requests with
htmx in a manner that should feel very familiar and in keeping with the
spirit of HTML, while also giving you the flexibility to achieve what
you want.</p>
<h2 id="_history_support">History Support</h2>
<p>We have a final piece of functionality to close out our overview of
htmx: browser history support. When you use normal HTML links and forms,
your browser will keep track of all the pages that you have visited. You
can then use the back button to navigate back to a previous page and,
once you have done this, you can use a forward button to go forward to
the original page you were on.</p>
<p>This notion of history was one of the killer features of the early
web. Unfortunately it turns out that history becomes tricky when you
move to the Single Page Application paradigm. An AJAX request does not,
by itself, register a web page in your browser’s history, which is a
good thing: an AJAX request may have nothing to do with the state of the
web page (perhaps it is just recording some activity in the browser), so
it wouldn’t be appropriate to create a new history entry for the
interaction.</p>
<p>However, there are likely to be a lot of AJAX driven interactions in
a Single Page Application where it <em class="test">is</em> appropriate to create a
history entry. There is a JavaScript API to work with browser history,
but this API is deeply annoying and difficult to work with, and thus
often ignored by JavaScript developers.</p>
<p>If you have ever used a Single Page Application and accidentally
clicked the back button, only to lose your entire application state and
have to start over, you have seen this problem in action.</p>
<p>In htmx, as with Single Page Application frameworks, you will often
need to explicitly work with the history API. Fortunately, since htmx
sticks so close to the native model of the web and since it is
declarative, getting web history right is typically much easier to do in
an htmx-based application.</p>
<p>Consider the button we have been looking at to load contacts:</p>
<figure>
<div class="sourceCode" id="cb17"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb17-1"><a aria-hidden="true" href="#cb17-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="dt">&gt;</span></span>
<span id="cb17-2"><a aria-hidden="true" href="#cb17-2" tabindex="-1"></a> Get The Contacts</span>
<span id="cb17-3"><a aria-hidden="true" href="#cb17-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our trusty button</p></figcaption>
</figure>
<p>As it stands, if you click this button it will retrieve the content
from <code>/contacts</code> and load it into the element with the id
<code>main</code>, but it will <em class="test">not</em> create a new history
entry.</p>
<p>If we wanted it to create a history entry when this request happened,
we would add a new attribute to the button, the <code>hx-push-url</code>
attribute:</p>
<figure>
<div class="sourceCode" id="cb18"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb18-1"><a aria-hidden="true" href="#cb18-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#main"</span><span class="ot"> hx-push-url</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb18-2"><a aria-hidden="true" href="#cb18-2" tabindex="-1"></a> Get The Contacts</span>
<span id="cb18-3"><a aria-hidden="true" href="#cb18-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our trusty button, now with history!</p></figcaption>
</figure>
<ol>
<li><p><code>hx-push-url</code> will create an entry in history when the
button is clicked.</p></li>
</ol>
<p>Now, when the button is clicked, the <code>/contacts</code> path will
be put into the browser’s navigation bar and a history entry will be
created for it. Furthermore, if the user clicks the back button, the
original content for the page will be restored, along with the original
URL.</p>
<p>Now, the name <code>hx-push-url</code> for this attribute might sound
a little obscure, but it is based on the JavaScript API,
<code>history.pushState()</code>. This notion of “pushing” derives from
the fact that history entries are modeled as a stack, and so you are
“pushing” new entries onto the top of the stack of history entries.</p>
<p>With this relatively simple, declarative mechanism, htmx allows you
to integrate with the back button in a way that mimics the “normal”
behavior of HTML.</p>
<p>Now, there is one additional thing we need to handle to get history
“just right”: we have “pushed” the <code>/contacts</code> path into the
browsers location bar successfully, and the back button works. But what
if someone refreshes their browser while on the <code>/contacts</code>
page?</p>
<p>In this case, you will need to handle the htmx-based “partial”
response as well as the non-htmx “full page” response. You can do this
using HTTP headers, a topic we will go into in detail later in the
book.</p>
<h2 id="_conclusion">Conclusion</h2>
<p>So that’s our whirlwind introduction to htmx. We’ve only seen about
ten attributes from the library, but you can see a hint of just how
powerful these attributes can be. Htmx enables a much more sophisticated
web application than is possible in plain HTML, with minimal additional
conceptual load compared to most JavaScript-based approaches.</p>
<p>Htmx aims to incrementally improve HTML as a hypermedia in a manner
that is conceptually coherent with the underlying markup language. Like
any technical choice, this is not without trade-offs: by staying so
close to HTML, htmx does not give developers a lot of infrastructure
that many might feel should be there “by default”.</p>
<p>By staying closer to the native model of the web, htmx aims to strike
a balance between simplicity and functionality, deferring to other
libraries for more elaborate frontend extensions on top of the existing
web platform. The good news is that htmx plays well with others, so when
these needs arise it is often easy enough to bring in another library to
handle them.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Budgeting For HTML</h2>
<p>The close relationship between content and markup means that good
HTML is labor-intensive. Most sites have a separation between the
authors, who are rarely familiar with HTML, and the developers, who need
to develop a generic system able to handle any content that’s thrown at
it — this separation usually taking the form of a CMS. As a result,
having markup tailored to content, which is often necessary for advanced
HTML, is rarely feasible.</p>
<p>Furthermore, for internationalized sites, content in different
languages being injected into the same elements can degrade markup
quality as stylistic conventions differ between languages. It’s an
expense few organizations can spare.</p>
<p>Thus, we don’t expect every site to contain perfectly conformant
HTML. What’s most important is to avoid <em class="test">wrong</em> HTML — it can be
better to fall back on a more generic element than to be precisely
incorrect.</p>
<p>If you have the resources, however, putting more care in your HTML
will produce a more polished site.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Htmx Patterns</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/htmx-patterns/#_installing_htmx">Installing Htmx</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_ajax_ifying_our_application">AJAX-ifying Our Application</a>
<ul>
<li>
<a href="https://hypermedia.systems/htmx-patterns/#_boosted_links">Boosted Links</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_boosted_forms">Boosted Forms</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_attribute_inheritance">Attribute Inheritance</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_progressive_enhancement">Progressive Enhancement</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_adding_hx_boost_to_contact_app">Adding “hx-boost” to
Contact.app</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_a_second_step_deleting_contacts_with_http_delete">A Second
Step: Deleting Contacts With HTTP DELETE</a>
<ul>
<li>
<a href="https://hypermedia.systems/htmx-patterns/#_updating_the_server_side_code">Updating The Server-Side
Code</a>
<ul>
<li>
<a href="https://hypermedia.systems/htmx-patterns/#_a_response_code_gotcha">A response code gotcha</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_targeting_the_right_element">Targeting The Right Element</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_updating_the_location_bar_url_properly">Updating The Location
Bar URL Properly</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_one_more_thing">One More Thing…​</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_progressive_enhancement_2">Progressive Enhancement?</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_next_steps_validating_contact_emails">Next Steps: Validating
Contact Emails</a>
<ul>
<li>
<a href="https://hypermedia.systems/htmx-patterns/#_updating_our_input_type">Updating Our Input Type</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_inline_validation">Inline Validation</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_validating_emails_server_side">Validating Emails
Server-Side</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_taking_the_user_experience_further">Taking The User Experience
Further</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_debouncing_our_validation_requests">Debouncing Our Validation
Requests</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_ignoring_non_mutating_keys">Ignoring Non-Mutating Keys</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_another_application_improvement_paging">Another Application
Improvement: Paging</a>
<ul>
<li>
<a href="https://hypermedia.systems/htmx-patterns/#_click_to_load">Click To Load</a>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#_infinite_scroll">Infinite Scroll</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/htmx-patterns/#html-note-title">HTML Notes: Caution With Modals and “display:
none”</a>
</li></ul>
</details>
<div class="division-content">
<p>Now that we’ve seen how htmx extends HTML as a hypermedia, it’s time
to put it into action. As we use htmx, we will still be using
hypermedia: we will issue HTTP requests and get back HTML. But, with the
additional functionality that htmx provides, we will have a more
<em class="test">powerful hypermedia</em> to work with, allowing us to accomplish
much more sophisticated interfaces.</p>
<p>This will allow us to address user experience issues, such as long
feedback cycles or painful page refreshes, without needing to write
much, if any, JavaScript, and without creating a JSON API. Everything
will be implemented in hypermedia, using the core hypermedia concepts of
the early web.</p>
<h2 id="_installing_htmx">Installing Htmx</h2>
<p>The first thing we need to do is install htmx in our web application.
We are going to do this by downloading the source and saving it locally
in our application, so we aren’t dependent on any external systems. This
is known as “vendoring” the library. We can grab the latest version of
htmx by navigating our browser to
<code>https://unpkg.com/htmx.org</code>, which will redirect us to the
source of the latest version of the library.</p>
<p>We can save the content from this URL into the
<code>static/js/htmx.js</code> file in our project.</p>
<p>You can, of course, use a more sophisticated JavaScript package
manager such as Node Package Manager (NPM) or yarn to install htmx. You
do this by referring to its package name, <code>htmx.org</code>, in the
manner appropriate for your tool. However, htmx is very small
(approximately 12kb when compressed and zipped) and is dependency free,
so using it does not require an elaborate mechanism or build tool.</p>
<p>With htmx downloaded locally to our applications
<code>/static/js</code> directory, we can now load it in to our
application. We do this by adding the following <code>script</code> tag
to the <code>head</code> tag in our <code>layout.html</code> file, which
will make htmx available and active on every page in our
application:</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">script</span><span class="ot"> src</span><span class="op">=</span><span class="st">"/js/htmx.js"</span><span class="dt">&gt;&lt;/</span><span class="kw">script</span><span class="dt">&gt;</span></span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a> ...</span>
<span id="cb1-4"><a aria-hidden="true" href="#cb1-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">head</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Installing htmx</p></figcaption>
</figure>
<p>Recall that the <code>layout.html</code> file is a <em class="test">layout</em>
file included in most templates that wraps the content of those
templates in common HTML, including a <code>head</code> element that we
are using here to install htmx.</p>
<p>Believe it or not, that’s it! This simple script tag will make htmx’s
functionality available across our entire application.</p>
<h2 id="_ajax_ifying_our_application">AJAX-ifying Our Application</h2>
<p>To get our feet wet with htmx, the first feature we are going to take
advantage of is known as “boosting.” This is a bit of a “magic” feature
in that we don’t need to do much beyond adding a single attribute,
<code>hx-boost</code>, to the application.</p>
<p>When you put <code>hx-boost</code> on a given element with the value
<code>true</code>, it will “boost” all anchor and form elements within
that element. “Boost”, here, means that htmx will convert all those
anchors and forms from “normal” hypermedia controls into AJAX-powered
hypermedia controls. Rather than issuing “normal” HTTP requests that
replace the whole page, the links and forms will issue AJAX requests.
Htmx then swaps the inner content of the <code>&lt;body&gt;</code> tag
in the response to these requests into the existing pages
<code>&lt;body&gt;</code> tag.</p>
<p>This makes navigation feel faster because the browser will not be
re-interpreting most of the tags in the response
<code>&lt;head&gt;</code> and so forth.</p>
<h3 id="_boosted_links">Boosted Links</h3>
<p>Let’s take a look at an example of a boosted link. Below is a link to
a hypothetical settings page for a web application. Because it has
<code>hx-boost="true"</code> on it, htmx will halt the normal link
behavior of issuing a request to the <code>/settings</code> path and
replacing the entire page with the response. Instead, htmx will issue an
AJAX request to <code>/settings</code>, take the result and replace the
<code>body</code> element with the new content.</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/settings"</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span>Settings<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
<figcaption><p>A boosted link</p></figcaption>
</figure>
<ol>
<li><p>The <code>hx-boost</code> attribute makes this link
AJAX-powered.</p></li>
</ol>
<p>You might reasonably ask: what’s the advantage here? We are issuing
an AJAX request and simply replacing the entire body.</p>
<p>Is that significantly different from just issuing a normal link
request?</p>
<p>Yes, it is in fact different: with a boosted link, the browser is
able to avoid any processing associated with the head tag. The head tag
often contains many scripts and CSS file references. In the boosted
scenario, it is not necessary to re-process those resources: the scripts
and styles have already been processed and will continue to apply to the
new content. This can often be a very easy way to speed up your
hypermedia application.</p>
<p>A second question you might have is: does the response need to be
formatted specially to work with <code>hx-boost</code>? After all, the
settings page would normally render an <code>html</code> tag, with a
<code>head</code> tag and so forth. Do you need to handle “boosted”
requests specially?</p>
<p>The answer is no: htmx is smart enough to pull out only the content
of the <code>body</code> tag to swap in to the new page. The
<code>head</code> tag is mostly ignored: only the title tag, if it is
present, will be processed. This means you don’t need to do anything
special on the server side to render templates that
<code>hx-boost</code> can handle: just return the normal HTML for your
page, and it should work fine.</p>
<p>Note that boosted links (and forms) will also continue to update the
navigation bar and history, just like normal links, so users will be
able to use the browser back button, will be able to copy and paste URLs
(or “deep links”) and so on. Links will act pretty much like “normal”,
they will just be faster.</p>
<h3 id="_boosted_forms">Boosted Forms</h3>
<p>Boosted form tags work in a similar way to boosted anchor tags: a
boosted form will use an AJAX request rather than the usual
browser-issued request, and will replace the entire body with the
response.</p>
<p>Here is an example of a form that posts messages to the
<code>/messages</code> endpoint using an HTTP <code>POST</code> request.
By adding <code>hx-boost</code> to it, those requests will be done in
AJAX, rather than the normal browser behavior.</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/messages"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"post"</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"message"</span><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Enter A Message..."</span><span class="dt">&gt;</span></span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="dt">&gt;</span>Post Your Message<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A boosted form</p></figcaption>
</figure>
<ol>
<li><p>As with the link, <code>hx-boost</code> makes this form
AJAX-powered.</p></li>
</ol>
<p>A big advantage of the AJAX-based request that <code>hx-boost</code>
uses (and the lack of head processing that occurs) is that it avoids
what is known as a <em class="test">flash of unstyled content</em>:</p>
<dl>
<dt>Flash Of Unstyled Content (FOUC)</dt>
<dd>
<p>A situation where a browser renders a web page before all the styling
information is available for the page. A FOUC causes a disconcerting
momentary “flash” of the unstyled content, which is then restyled when
all the style information is available. You will notice this as a
flicker when you move around the internet: text, images and other
content can “jump around” on the page as styles are applied to it.</p>
</dd>
</dl>
<p>With <code>hx-boost</code> the site’s styling is already loaded
<em class="test">before</em> the new content is retrieved, so there is no such flash
of unstyled content. This can make a “boosted” application feel both
smoother and also snappier in general.</p>
<h3 id="_attribute_inheritance">Attribute Inheritance</h3>
<p>Let’s expand on our previous example of a boosted link, and add a few
more boosted links alongside it. We’ll add links so that we have one to
the <code>/contacts</code> page, the <code>/settings</code> page, and
the <code>/help</code> page. All these links are boosted and will behave
in the manner that we have described above.</p>
<p>This feels a little redundant, doesn’t it? It seems silly to annotate
all three links with the <code>hx-boost="true"</code> attribute right
next to one another.</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span>Contacts<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/settings"</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span>Settings<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/help"</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span>Help<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A set of boosted links</p></figcaption>
</figure>
<p>Htmx offers a feature to help reduce this redundancy: attribute
inheritance. With most attributes in htmx, if you place it on a parent,
the attribute will also apply to children elements. This is how
Cascading Style Sheets work, and that idea inspired htmx to adopt a
similar “cascading htmx attributes” feature.</p>
<p>To avoid the redundancy in this example, let’s introduce a
<code>div</code> element that encloses all the links and then “hoist”
the <code>hx-boost</code> attribute up to that parent <code>div</code>.
This will let us remove the redundant <code>hx-boost</code> attributes
but ensure all the links are still boosted, inheriting that
functionality from the parent <code>div</code>.</p>
<p>Note that any legal HTML element could be used here, we just use a
<code>div</code> out of habit.</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts"</span><span class="dt">&gt;</span>Contacts<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/settings"</span><span class="dt">&gt;</span>Settings<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/help"</span><span class="dt">&gt;</span>Help<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Boosting links via the parent</p></figcaption>
</figure>
<ol>
<li><p>The <code>hx-boost</code> has been moved to the parent
div.</p></li>
</ol>
<p>Now we don’t have to put an <code>hx-boost="true"</code> on every
link and, in fact, we can add more links alongside the existing ones,
and they, too, will be boosted, without us needing to explicitly
annotate them.</p>
<p>That’s fine, but what if you have a link that you <em class="test">don’t</em> want
boosted within an element that has <code>hx-boost="true"</code> on it? A
good example of this situation is when a link is to a resource to be
downloaded, such as a PDF. Downloading a file can’t be handled well by
an AJAX request, so you probably want that link to behave “normally”,
issuing a full page request for the PDF, which the browser will then
offer to save as a file on the user’s local system.</p>
<p>To handle this situation, you simply override the parent
<code>hx-boost</code> value with <code>hx-boost="false"</code> on the
anchor tag that you don’t want to boost:</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts"</span><span class="dt">&gt;</span>Contacts<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/settings"</span><span class="dt">&gt;</span>Settings<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/help"</span><span class="dt">&gt;</span>Help<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb6-5"><a aria-hidden="true" href="#cb6-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/help/documentation.pdf"</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"false"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb6-6"><a aria-hidden="true" href="#cb6-6" tabindex="-1"></a> Download Docs</span>
<span id="cb6-7"><a aria-hidden="true" href="#cb6-7" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb6-8"><a aria-hidden="true" href="#cb6-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Disabling boosting</p></figcaption>
</figure>
<ol>
<li><p>The <code>hx-boost</code> is still on the parent div.</p></li>
<li><p>The boosting behavior is overridden for this link.</p></li>
</ol>
<p>Here we have a new link to a documentation PDF that we wish to
function like a regular link. We have added
<code>hx-boost="false"</code> to the link and this declaration will
override the <code>hx-boost="true"</code> on the parent
<code>div</code>, reverting it to regular link behavior and, thus,
allowing for the file download behavior that we want.</p>
<h3 id="_progressive_enhancement">Progressive Enhancement</h3>
<p>A nice aspect of <code>hx-boost</code> is that it is an example of
<em class="test">progressive enhancement</em>:</p>
<dl>
<dt>Progressive Enhancement</dt>
<dd>
<p>A software design philosophy that aims to provide as much essential
content and functionality to as many users as possible, while delivering
a better experience to users with more advanced web browsers.</p>
</dd>
</dl>
<p>Consider the links in the example above. What would happen if someone
did not have JavaScript enabled?</p>
<p>No problem. The application would continue to work, but it would
issue regular HTTP requests, rather than AJAX-based HTTP requests. This
means that your web application will work for the maximum number of
users; those with modern browsers (or users who have not turned off
JavaScript) can take advantage of the benefits of the AJAX-style
navigation that htmx offers, and others can still use the app just
fine.</p>
<p>Compare the behavior of htmx’s <code>hx-boost</code> attribute with a
JavaScript heavy Single Page Application: such an application often
won’t function <em class="test">at all</em> without JavaScript enabled. It is often
very difficult to adopt a progressive enhancement approach when you use
an SPA framework.</p>
<p>This is <em class="test">not</em> to say that every htmx feature offers
progressive enhancement. It is certainly possible to build features that
do not offer a “No JS” fallback in htmx, and, in fact, many of the
features we will build later in the book will fall into this category.
We will note when a feature is progressive enhancement friendly and when
it is not.</p>
<p>Ultimately, it is up to you, the developer, to decide if the
trade-offs of progressive enhancement (a more basic UX, limited
improvements over plain HTML) are worth the benefits for your
application users.</p>
<h3 id="_adding_hx_boost_to_contact_app">Adding “hx-boost” to
Contact.app</h3>
<p>For the contact app we are building, we want this htmx “boost”
behavior…​ well, everywhere.</p>
<p>Right? Why not?</p>
<p>How could we accomplish that?</p>
<p>Well, it’s easy (and pretty common in htmx-powered web applications):
we can just add <code>hx-boost</code> on the <code>body</code> tag of
our <code>layout.html</code> template, and we are done.</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">html</span><span class="dt">&gt;</span></span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a>...</span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">body</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a>...</span>
<span id="cb7-5"><a aria-hidden="true" href="#cb7-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span id="cb7-6"><a aria-hidden="true" href="#cb7-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">html</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Boosting the entire contact.app</p></figcaption>
</figure>
<ol>
<li><p>All links and forms will be boosted now!</p></li>
</ol>
<p>Now every link and form in our application will use AJAX by default,
making it feel much snappier. Consider the “New Contact” link that we
created on the main page:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/new"</span><span class="dt">&gt;</span>Add Contact<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A newly boosted “add contact” link</p></figcaption>
</figure>
<p>Even though we haven’t touched anything on this link or on the
server-side handling of the URL it targets, it will now “just work” as a
boosted link, using AJAX for a snappier user experience, including
updating history, back button support and so on. And, if JavaScript
isn’t enabled, it will fall back to the normal link behavior.</p>
<p>All this with one htmx attribute.</p>
<p>The <code>hx-boost</code> attribute is neat, but is different than
other htmx attributes in that it is pretty “magical”: by making one
small change you modify the behavior of a large number of elements on
the page, turning them into AJAX-powered elements. Most other htmx
attributes are generally lower level and require more explicit
annotations in order to specify exactly what you want htmx to do. In
general, this is the design philosophy of htmx: prefer explicit over
implicit and obvious over “magic.”</p>
<p>However, the <code>hx-boost</code> attribute was too useful to allow
dogma to override practicality, and so it is included as a feature in
the library.</p>
<h2 id="_a_second_step_deleting_contacts_with_http_delete">A Second
Step: Deleting Contacts With HTTP DELETE</h2>
<p>For our next step with htmx, recall that Contact.app has a small form
on the edit page of a contact that is used to delete the contact:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/delete"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"post"</span><span class="dt">&gt;</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="dt">&gt;</span>Delete Contact<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Plain HTML form to delete a contact</p></figcaption>
</figure>
<p>This form issued an HTTP <code>POST</code> to, for example,
<code>/contacts/42/delete</code>, in order to delete the contact with
the ID 42.</p>
<p>We mentioned previously that one of the annoying things about HTML is
that you can’t issue an HTTP <code>DELETE</code> (or <code>PUT</code> or
<code>PATCH</code>) request directly, even though these are all part of
HTTP and HTTP is <em class="test">obviously designed</em> for transferring HTML.</p>
<p>Thankfully, now, with htmx, we have a chance to rectify this
situation.</p>
<p>The “right thing,” from a RESTful, resource-oriented perspective is,
rather than issuing an HTTP <code>POST</code> to
<code>/contacts/42/delete</code>, to issue an HTTP <code>DELETE</code>
to <code>/contacts/42</code>. We want to delete the contact. The contact
is a resource. The URL for that resource is <code>/contacts/42</code>.
So the ideal is a <code>DELETE</code> request to
<code>/contacts/42/</code>.</p>
<p>Let’s update our application to do this by adding the htmx
<code>hx-delete</code> attribute to the “Delete Contact” button:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>Delete Contact<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>An htmx-powered button for deleting a
contact</p></figcaption>
</figure>
<p>Now, when a user clicks this button, htmx will issue an HTTP
<code>DELETE</code> request via AJAX to the URL for the contact in
question.</p>
<p>A couple of things to notice:</p>
<ul>
<li><p>We no longer need a <code>form</code> tag to wrap the button,
because the button itself carries the hypermedia action that it performs
directly on itself.</p></li>
<li><p>We no longer need to use the somewhat awkward
<code>"/contacts/{{ contact.id }}/delete"</code> route, but can simply
use the <code>"/contacts/{{ contact.id }}</code> route, since we are
issuing a <code>DELETE</code>. By using a <code>DELETE</code> we
disambiguate between a request intended to update the contact and a
request intended to delete it, using the native HTTP tools available for
exactly this reason.</p></li>
</ul>
<p>Note that we have done something pretty magical here: we have turned
this button into a <em class="test">hypermedia control</em>. It is no longer
necessary that this button be placed within a larger <code>form</code>
tag in order to trigger an HTTP request: it is a stand-alone, and fully
featured hypermedia control on its own. This is at the heart of htmx,
allowing any element to become a hypermedia control and fully
participate in a Hypermedia-Driven Application.</p>
<p>We should also note that, unlike with the <code>hx-boost</code>
examples above, this solution will <em class="test">not</em> degrade gracefully. To
make this solution degrade gracefully, we would need to wrap the button
in a form element and handle a <code>POST</code> on the server side as
well.</p>
<p>In the interest of keeping our application simple, we are going to
omit that more elaborate solution.</p>
<h3 id="_updating_the_server_side_code">Updating The Server-Side
Code</h3>
<p>We have updated the client-side code (if HTML can be considered code)
so it now issues a <code>DELETE</code> request to the appropriate URL,
but we still have some work to do. Since we updated both the route and
the HTTP method we are using, we are going to need to update the
server-side implementation as well to handle this new HTTP request.</p>
<figure>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;/delete"</span>, methods<span class="op">=</span>[<span class="st">"POST"</span>])</span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a><span class="kw">def</span> contacts_delete(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a> contact.delete()</span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a> flash(<span class="st">"Deleted Contact!"</span>)</span>
<span id="cb11-6"><a aria-hidden="true" href="#cb11-6" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts"</span>)</span></code></pre></div>
<figcaption><p>The original server-side code for deleting a
contact</p></figcaption>
</figure>
<p>We’ll need to make two changes to our handler: update the route, and
update the HTTP method we are using to delete contacts.</p>
<figure>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a aria-hidden="true" href="#cb12-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;"</span>, methods<span class="op">=</span>[<span class="st">"DELETE"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb12-2"><a aria-hidden="true" href="#cb12-2" tabindex="-1"></a><span class="kw">def</span> contacts_delete(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb12-3"><a aria-hidden="true" href="#cb12-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb12-4"><a aria-hidden="true" href="#cb12-4" tabindex="-1"></a> contact.delete()</span>
<span id="cb12-5"><a aria-hidden="true" href="#cb12-5" tabindex="-1"></a> flash(<span class="st">"Deleted Contact!"</span>)</span>
<span id="cb12-6"><a aria-hidden="true" href="#cb12-6" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts"</span>)</span></code></pre></div>
<figcaption><p>Updated handler with new route and
method</p></figcaption>
</figure>
<ol>
<li><p>An updated path and method for the handler.</p></li>
</ol>
<p>Pretty simple, and much cleaner.</p>
<h4 id="_a_response_code_gotcha">A response code gotcha</h4>
<p>Unfortunately, there is a problem with our updated handler: by
default, in Flask the <code>redirect()</code> method responds with a
<code>302 Found</code> HTTP Response Code.</p>
<p>According to the Mozilla Developer Network (MDN) web docs on the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302"><code>302 Found</code></a>
response, this means that the HTTP <em class="test">method</em> of the request
<em class="test">will be unchanged</em> when the redirected HTTP request is
issued.</p>
<p>We are now issuing a <code>DELETE</code> request with htmx and then
being redirected to the <code>/contacts</code> path by flask. According
to this logic, that would mean that the redirected HTTP request would
still be a <code>DELETE</code> method. This means that, as it stands,
the browser will issue a <code>DELETE</code> request to
<code>/contacts</code>.</p>
<p>This is definitely <em class="test">not</em> what we want: we would like the HTTP
redirect to issue a <code>GET</code> request, slightly modifying the
Post/Redirect/Get behavior we discussed earlier to be a
Delete/Redirect/Get.</p>
<p>Fortunately, there is a different response code, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303"><code>303 See Other</code></a>,
that does what we want: when a browser receives a
<code>303 See Other</code> redirect response, it will issue a
<code>GET</code> to the new location.</p>
<p>So we want to update our code to use the <code>303</code> response
code in the controller.</p>
<p>Thankfully, this is very easy: there is a second parameter to
<code>redirect()</code> that takes the numeric response code you wish to
send.</p>
<figure>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a aria-hidden="true" href="#cb13-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;"</span>, methods<span class="op">=</span>[<span class="st">"DELETE"</span>])</span>
<span id="cb13-2"><a aria-hidden="true" href="#cb13-2" tabindex="-1"></a><span class="kw">def</span> contacts_delete(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb13-3"><a aria-hidden="true" href="#cb13-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb13-4"><a aria-hidden="true" href="#cb13-4" tabindex="-1"></a> contact.delete()</span>
<span id="cb13-5"><a aria-hidden="true" href="#cb13-5" tabindex="-1"></a> flash(<span class="st">"Deleted Contact!"</span>)</span>
<span id="cb13-6"><a aria-hidden="true" href="#cb13-6" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts"</span>, <span class="dv">303</span>) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Updated handler with <code>303</code> redirect
response</p></figcaption>
</figure>
<ol>
<li><p>The response code is now a 303.</p></li>
</ol>
<p>Now, when you want to remove a given contact, you can simply issue a
<code>DELETE</code> to the same URL as you used to access the contact in
the first place.</p>
<p>This is a natural HTTP-based approach to deleting a resource.</p>
<h3 id="_targeting_the_right_element">Targeting The Right Element</h3>
<p>We aren’t quite finished with our updated delete button. Recall that,
by default, htmx “targets” the element that triggers a request, and will
place the HTML returned by the server inside that element. Right now,
the “Delete Contact” button is targeting itself.</p>
<p>That means that, since the redirect to the <code>/contacts</code> URL
is going to re-render the entire contact list, we will end up with that
contact list placed <em class="test">inside</em> the “Delete Contact” button.</p>
<p>Mis-targeting like this comes up from time to time when you are
working with htmx and can lead to some pretty funny situations.</p>
<p>The fix for this is easy: add an explicit target to the button, and
target the <code>body</code> element with the response:</p>
<figure>
<div class="sourceCode" id="cb14"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb14-1"><a aria-hidden="true" href="#cb14-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span></span>
<span id="cb14-2"><a aria-hidden="true" href="#cb14-2" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb14-3"><a aria-hidden="true" href="#cb14-3" tabindex="-1"></a> Delete Contact</span>
<span id="cb14-4"><a aria-hidden="true" href="#cb14-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A fixed htmx-powered button for deleting a
contact</p></figcaption>
</figure>
<ol>
<li><p>An explicit target added to the button.</p></li>
</ol>
<p>Now our button behaves as expected: clicking on the button will issue
an HTTP <code>DELETE</code> to the server against the URL for the
current contact, delete the contact and redirect back to the contact
list page, with a nice flash message.</p>
<p>Is everything working smoothly now?</p>
<h3 id="_updating_the_location_bar_url_properly">Updating The Location
Bar URL Properly</h3>
<p>Well, almost.</p>
<p>If you click on the button you will notice that, despite the
redirect, the URL in the location bar is not correct. It still points to
<code>/contacts/{{ contact.id }}</code>. That’s because we haven’t told
htmx to update the URL: it just issues the <code>DELETE</code> request
and then updates the DOM with the response.</p>
<p>As we mentioned, boosting via <code>hx-boost</code> will naturally
update the location bar for you, mimicking normal anchors and forms, but
in this case we are building a custom button hypermedia control to issue
a <code>DELETE</code>. We need to let htmx know that we want the
resulting URL from this request “pushed” into the location bar.</p>
<p>We can achieve this by adding the <code>hx-push-url</code> attribute
with the value <code>true</code> to our button:</p>
<figure>
<div class="sourceCode" id="cb15"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb15-1"><a aria-hidden="true" href="#cb15-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span></span>
<span id="cb15-2"><a aria-hidden="true" href="#cb15-2" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span></span>
<span id="cb15-3"><a aria-hidden="true" href="#cb15-3" tabindex="-1"></a><span class="ot"> hx-push-url</span><span class="op">=</span><span class="st">"true"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb15-4"><a aria-hidden="true" href="#cb15-4" tabindex="-1"></a> Delete Contact</span>
<span id="cb15-5"><a aria-hidden="true" href="#cb15-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Deleting a contact, now with proper location
information</p></figcaption>
</figure>
<ol>
<li><p>We tell htmx to push the redirected URL up into the location
bar.</p></li>
</ol>
<p><em class="test">Now</em> we are done.</p>
<p>We have a button that, all by itself, is able to issue a properly
formatted HTTP <code>DELETE</code> request to the correct URL, and the
UI and location bar are all updated correctly. This was accomplished
with three declarative attributes placed directly on the button:
<code>hx-delete</code>, <code>hx-target</code> and
<code>hx-push-url</code>.</p>
<p>This required more work than the <code>hx-boost</code> change, but
the explicit code makes it easy to see what the button is doing as a
custom hypermedia control. The resulting solution feels clean; it takes
advantage of the built-in features of the web as a hypermedia system
without any URL hacks.</p>
<h3 id="_one_more_thing">One More Thing…​</h3>
<p>There is one additional “bonus” feature we can add to our “Delete
Contact” button: a confirmation dialog. Deleting a contact is a
destructive operation and as it stands right now, if the user
inadvertently clicked the “Delete Contact” button, the application would
just delete that contact. Too bad, so sad for the user.</p>
<p>Fortunately htmx has an easy mechanism for adding a confirmation
message on destructive operations like this: the <code>hx-confirm</code>
attribute. You can place this attribute on an element, with a message as
its value, and the JavaScript method <code>confirm()</code> will be
called before a request is issued, which will show a simple confirmation
dialog to the user asking them to confirm the action. Very easy and a
great way to prevent accidents.</p>
<p>Here is how we would add confirmation of the contact delete
operation:</p>
<figure>
<div class="sourceCode" id="cb16"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb16-1"><a aria-hidden="true" href="#cb16-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span></span>
<span id="cb16-2"><a aria-hidden="true" href="#cb16-2" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span></span>
<span id="cb16-3"><a aria-hidden="true" href="#cb16-3" tabindex="-1"></a><span class="ot"> hx-push-url</span><span class="op">=</span><span class="st">"true"</span></span>
<span id="cb16-4"><a aria-hidden="true" href="#cb16-4" tabindex="-1"></a><span class="ot"> hx-confirm</span><span class="op">=</span><span class="st">"Are you sure you want to delete this contact?"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb16-5"><a aria-hidden="true" href="#cb16-5" tabindex="-1"></a> Delete Contact</span>
<span id="cb16-6"><a aria-hidden="true" href="#cb16-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Confirming deletion</p></figcaption>
</figure>
<ol>
<li><p>This message will be shown to the user, asking them to confirm
the delete.</p></li>
</ol>
<p>Now, when someone clicks on the “Delete Contact” button, they will be
presented with a prompt that asks “Are you sure you want to delete this
contact?” and they will have an opportunity to cancel if they clicked
the button in error. Very nice.</p>
<p>With this final change we now have a pretty solid “delete contact”
mechanism: we are using the correct RESTful routes and HTTP Methods, we
are confirming the deletion, and we have removed a lot of the cruft that
normal HTML imposes on us, all while using declarative attributes in our
HTML and staying firmly within the normal hypermedia model of the
web.</p>
<h3 id="_progressive_enhancement_2">Progressive Enhancement?</h3>
<p>As we noted earlier about this solution: it is <em class="test">not</em> a
progressive enhancement to our web application. If someone has disabled
JavaScript then this “Delete Contact” button will no longer work. We
would need to do additional work to keep the older form-based mechanism
working in a JavaScript-disabled environment.</p>
<p>Progressive Enhancement can be a hot-button topic in web development,
with lots of passionate opinions and perspectives. Like nearly all
JavaScript libraries, htmx makes it possible to create applications that
do not function in the absence of JavaScript. Retaining support for
non-JavaScript clients requires additional work and complexity in your
application. It is important to determine exactly how important
supporting non-JavaScript clients is before you begin using htmx, or any
other JavaScript framework, for improving your web applications.</p>
<h2 id="_next_steps_validating_contact_emails">Next Steps: Validating
Contact Emails</h2>
<p>Let’s move on to another improvement in our application. A big part
of any web app is validating the data that is submitted to the server:
ensuring emails are correctly formatted and unique, numeric values are
valid, dates are acceptable, and so forth.</p>
<p>Currently, our application has a small amount of validation that is
done entirely server-side and that displays an error message when an
error is detected.</p>
<p>We are not going to go into the details of how validation works in
the model objects, but recall what the code for updating a contact looks
like from Chapter 3:</p>
<figure>
<div class="sourceCode" id="cb17"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb17-1"><a aria-hidden="true" href="#cb17-1" tabindex="-1"></a><span class="kw">def</span> contacts_edit_post(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb17-2"><a aria-hidden="true" href="#cb17-2" tabindex="-1"></a> c <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb17-3"><a aria-hidden="true" href="#cb17-3" tabindex="-1"></a> c.update(</span>
<span id="cb17-4"><a aria-hidden="true" href="#cb17-4" tabindex="-1"></a> request.form[<span class="st">'first_name'</span>],</span>
<span id="cb17-5"><a aria-hidden="true" href="#cb17-5" tabindex="-1"></a> request.form[<span class="st">'last_name'</span>],</span>
<span id="cb17-6"><a aria-hidden="true" href="#cb17-6" tabindex="-1"></a> request.form[<span class="st">'phone'</span>],</span>
<span id="cb17-7"><a aria-hidden="true" href="#cb17-7" tabindex="-1"></a> request.form[<span class="st">'email'</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb17-8"><a aria-hidden="true" href="#cb17-8" tabindex="-1"></a> <span class="cf">if</span> c.save():</span>
<span id="cb17-9"><a aria-hidden="true" href="#cb17-9" tabindex="-1"></a> flash(<span class="st">"Updated Contact!"</span>)</span>
<span id="cb17-10"><a aria-hidden="true" href="#cb17-10" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts/"</span> <span class="op">+</span> <span class="bu">str</span>(contact_id))</span>
<span id="cb17-11"><a aria-hidden="true" href="#cb17-11" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb17-12"><a aria-hidden="true" href="#cb17-12" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"edit.html"</span>, contact<span class="op">=</span>c) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Server-side validation on contact update</p></figcaption>
</figure>
<ol>
<li><p>We attempt to save the contact.</p></li>
<li><p>If the save does not succeed we re-render the form to display
error messages.</p></li>
</ol>
<p>So we attempt to save the contact, and, if the <code>save()</code>
method returns true, we redirect to the contact’s detail page. If the
<code>save()</code> method does not return true, that indicates that
there was a validation error; instead of redirecting, we re-render the
HTML for editing the contact. This gives the user a chance to correct
the errors, which are displayed alongside the inputs.</p>
<p>Let’s take a look at the HTML for the email input:</p>
<figure>
<div class="sourceCode" id="cb18"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb18-1"><a aria-hidden="true" href="#cb18-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb18-2"><a aria-hidden="true" href="#cb18-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb18-3"><a aria-hidden="true" href="#cb18-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"text"</span></span>
<span id="cb18-4"><a aria-hidden="true" href="#cb18-4" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Email"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.email }}"</span><span class="dt">&gt;</span></span>
<span id="cb18-5"><a aria-hidden="true" href="#cb18-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb18-6"><a aria-hidden="true" href="#cb18-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Validation error messages</p></figcaption>
</figure>
<ol>
<li><p>Display any errors associated with the email field</p></li>
</ol>
<p>We have a label for the input, an input of type <code>text</code> and
then a bit of HTML to display any error messages associated with the
email. When the template is rendered on the server, if there are errors
associated with the contact’s email, they will be displayed in this
span, which will be highlighted red.</p>
<div id="sidebar">
<div>
<div>
<p><strong>Server-Side Validation Logic</strong></p>
</div>
<div>
<p>Right now there is a bit of logic in the contact class that checks if
there are any other contacts with the same email address, and adds an
error to the contact model if so, since we do not want to have duplicate
emails in the database. This is a very common validation example: emails
are usually unique and adding two contacts with the same email is almost
certainly a user error.</p>
<p>Again, we are not going into the details of how validation works in
our models, but almost all server-side frameworks provide ways to
validate data and collect errors to display to the user. This sort of
infrastructure is very common in Web 1.0 server-side frameworks.</p>
</div>
</div>
</div>
<p>The error message shown when a user attempts to save a contact with a
duplicate email is “Email Must Be Unique”, as seen in <a class="ref" href="#fig-emailerror">[fig-emailerror]</a>.</p>
<figure id="fig-emailerror">
<p><img src="https://hypermedia.systems/images/screenshot_validation_error.png"/></p>
<figcaption><p>Email validation error</p></figcaption>
</figure>
<p>All of this is done using plain HTML and using Web 1.0 techniques,
and it works well.</p>
<p>However, as the application currently stands, there are two
annoyances.</p>
<ul>
<li><p>First, there is no email format validation: you can enter
whatever characters you’d like as an email and, as long as they are
unique, the system will allow it.</p></li>
<li><p>Second, we only check the email’s uniqueness when all the data is
submitted: if a user has entered a duplicate email, they will not find
out until they have filled in all the fields. This could be quite
annoying if the user was accidentally reentering a contact and had to
put all the contact information in before being made aware of this
fact.</p></li>
</ul>
<h3 id="_updating_our_input_type">Updating Our Input Type</h3>
<p>For the first issue, we have a pure HTML mechanism for improving our
application: HTML 5 supports inputs of type <code>email</code>. All we
need to do is switch our input from type <code>text</code> to type
<code>email</code>, and the browser will enforce that the value entered
properly matches the email format:</p>
<figure>
<div class="sourceCode" id="cb19"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb19-1"><a aria-hidden="true" href="#cb19-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb19-2"><a aria-hidden="true" href="#cb19-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb19-3"><a aria-hidden="true" href="#cb19-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb19-4"><a aria-hidden="true" href="#cb19-4" tabindex="-1"></a> placeholder="Email" value="{{ contact.email }}"&gt;</span>
<span id="cb19-5"><a aria-hidden="true" href="#cb19-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb19-6"><a aria-hidden="true" href="#cb19-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Changing the input to type
<code>email</code></p></figcaption>
</figure>
<ol>
<li><p>A change of the <code>type</code> attribute to <code>email</code>
ensures that values entered are valid emails.</p></li>
</ol>
<p>With this change, when the user enters a value that isn’t a valid
email, the browser will display an error message asking for a properly
formed email in that field.</p>
<p>So a simple single-attribute change done in pure HTML improves our
validation and addresses the first problem we noted.</p>
<div id="sidebar">
<div>
<div>
<p><strong>Server-Side vs. Client-Side Validations</strong></p>
</div>
<div>
<p>Experienced web developers might be grinding their teeth at the code
above: this validation is done on <em class="test">the client-side</em>. That is, we
are relying on the browser to detect the malformed email and correct the
user. Unfortunately, the client-side is not trustworthy: a browser may
have a bug in it that allows the user to circumvent this validation
code. Or, worse, the user may be malicious and figure out a mechanism
around our validation entirely, such as using the developer console to
edit the HTML.</p>
<p>This is a perpetual danger in web development: all validations done
on the client-side cannot be trusted and, if the validation is
important, <em class="test">must be redone</em> on the server-side. This is less of a
problem in Hypermedia-Driven Applications than in Single Page
Applications, because the focus of HDAs is the server-side, but it is
worth bearing in mind as you build your application.</p>
</div>
</div>
</div>
<h3 id="_inline_validation">Inline Validation</h3>
<p>While we have improved our validation experience a bit, the user must
still submit the form to get any feedback on duplicate emails. We can
next use htmx to improve this user experience.</p>
<p>It would be better if the user were able to see a duplicate email
error immediately after entering the email value. It turns out that
inputs fire a <code>change</code> event and, in fact, the
<code>change</code> event is the <em class="test">default trigger</em> for inputs in
htmx. So, putting this feature to work, we can implement the following
behavior: when the user enters an email, immediately issue a request to
the server and validate that email, and render an error message if
necessary.</p>
<p>Recall the current HTML for our email input:</p>
<figure>
<div class="sourceCode" id="cb20"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb20-1"><a aria-hidden="true" href="#cb20-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb20-2"><a aria-hidden="true" href="#cb20-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb20-3"><a aria-hidden="true" href="#cb20-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"email"</span></span>
<span id="cb20-4"><a aria-hidden="true" href="#cb20-4" tabindex="-1"></a><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Email"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.email }}"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb20-5"><a aria-hidden="true" href="#cb20-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb20-6"><a aria-hidden="true" href="#cb20-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The initial email configuration</p></figcaption>
</figure>
<ol>
<li><p>This is the input that we want to have drive an HTTP request to
validate the email.</p></li>
<li><p>This is the span we want to put the error message, if any,
into.</p></li>
</ol>
<p>So we want to add an <code>hx-get</code> attribute to this input.
This will cause the input to issue an HTTP <code>GET</code> request to a
given URL to validate the email. We then want to target the error span
following the input with any error message returned from the server.</p>
<p>Let’s make those changes to our HTML:</p>
<figure>
<div class="sourceCode" id="cb21"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb21-1"><a aria-hidden="true" href="#cb21-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb21-2"><a aria-hidden="true" href="#cb21-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb21-3"><a aria-hidden="true" href="#cb21-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"email"</span></span>
<span id="cb21-4"><a aria-hidden="true" href="#cb21-4" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/email"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb21-5"><a aria-hidden="true" href="#cb21-5" tabindex="-1"></a> hx-target="next .error" <span class="er">&lt;</span>2&gt;</span>
<span id="cb21-6"><a aria-hidden="true" href="#cb21-6" tabindex="-1"></a> placeholder="Email" value="{{ contact.email }}"&gt;</span>
<span id="cb21-7"><a aria-hidden="true" href="#cb21-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb21-8"><a aria-hidden="true" href="#cb21-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our updated HTML</p></figcaption>
</figure>
<ol>
<li><p>Issue an HTTP <code>GET</code> to the <code>email</code> endpoint
for the contact.</p></li>
<li><p>Target the next element with the class <code>error</code> on
it.</p></li>
</ol>
<p>Note that in the <code>hx-target</code> attribute we are using a
<em class="test">relative positional</em> selector, <code>next</code>. This is a
feature of htmx and an extension to normal CSS. Htmx supports prefixes
that will find targets <em class="test">relative</em> to the current element.</p>
<div id="sidebar">
<div>
<div>
<p><strong>Relative Positional Expressions in Htmx</strong></p>
</div>
<div>
<dl>
<dt><code>next</code></dt>
<dd>
<p>Scan forward in the DOM for the next matching element, e.g.,
<code>next .error</code></p>
</dd>
<dt><code>previous</code></dt>
<dd>
<p>Scan backwards in the DOM for the closest previous matching element,
e.g., <code>previous .alert</code></p>
</dd>
<dt><code>closest</code></dt>
<dd>
<p>Scan the parents of this element for matching element, e.g.,
<code>closest table</code></p>
</dd>
<dt><code>find</code></dt>
<dd>
<p>Scan the children of this element for matching element, e.g.,
<code>find span</code></p>
</dd>
<dt><code>this</code></dt>
<dd>
<p>the current element is the target (default)</p>
</dd>
</dl>
</div>
</div>
</div>
<p>By using relative positional expressions we can avoid adding explicit
ids to elements and take advantage of the local structure of HTML.</p>
<p>So, in our example with added <code>hx-get</code> and
<code>hx-target</code> attributes, whenever someone changes the value of
the input (remember, <code>change</code> is the <em class="test">default</em> trigger
for inputs in htmx) an HTTP <code>GET</code> request will be issued to
the given URL. If there are any errors, they will be loaded into the
error span.</p>
<h3 id="_validating_emails_server_side">Validating Emails
Server-Side</h3>
<p>Next, let’s look at the server-side implementation. We are going to
add another endpoint, similar to our edit endpoint in some ways: it is
going to look up the contact based on the ID encoded in the URL. In this
case, however, we only want to update the email of the contact, and we
obviously don’t want to save it! Instead, we will call the
<code>validate()</code> method on it.</p>
<p>That method will validate the email is unique and so forth. At that
point we can return any errors associated with the email directly, or
the empty string if none exist.</p>
<figure>
<div class="sourceCode" id="cb22"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb22-1"><a aria-hidden="true" href="#cb22-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;/email"</span>, methods<span class="op">=</span>[<span class="st">"GET"</span>])</span>
<span id="cb22-2"><a aria-hidden="true" href="#cb22-2" tabindex="-1"></a><span class="kw">def</span> contacts_email_get(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb22-3"><a aria-hidden="true" href="#cb22-3" tabindex="-1"></a> c <span class="op">=</span> Contact.find(contact_id) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb22-4"><a aria-hidden="true" href="#cb22-4" tabindex="-1"></a> c.email <span class="op">=</span> request.args.get(<span class="st">'email'</span>) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb22-5"><a aria-hidden="true" href="#cb22-5" tabindex="-1"></a> c.validate() <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb22-6"><a aria-hidden="true" href="#cb22-6" tabindex="-1"></a> <span class="cf">return</span> c.errors.get(<span class="st">'email'</span>) <span class="kw">or</span> <span class="st">""</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Code for our email validation endpoint</p></figcaption>
</figure>
<ol>
<li><p>Look up the contact by id.</p></li>
<li><p>Update its email (note that since this is a <code>GET</code>, we
use the <code>args</code> property rather than the <code>form</code>
property).</p></li>
<li><p>Validate the contact.</p></li>
<li><p>Return a string, either the errors associated with the email
field or, if there are none, the empty string.</p></li>
</ol>
<p>With this small bit of server-side code in place, we now have the
following user experience: when a user enters an email and tabs to the
next input field, they are immediately notified if the email is already
taken.</p>
<p>Note that the email validation is <em class="test">still</em> done when the entire
contact is submitted for an update, so there is no danger of allowing
duplicate email contacts to slip through: we have simply made it
possible for users to catch this situation earlier by use of htmx.</p>
<p>It is also worth noting that this particular email validation
<em class="test">must</em> be done on the server side: you cannot determine that an
email is unique across all contacts unless you have access to the data
store of record. This is another simplifying aspect of Hypermedia-Driven
Applications: since validations are done server-side, you have access to
all the data you might need to do any sort of validation you’d like.</p>
<p>Here again we want to stress that this interaction is done entirely
within the hypermedia model: we are using declarative attributes and
exchanging hypermedia with the server in a manner very similar to how
links or forms work. But we have managed to improve our user experience
dramatically.</p>
<h3 id="_taking_the_user_experience_further">Taking The User Experience
Further</h3>
<p>Despite the fact that we haven’t added a lot of code here, we have a
fairly sophisticated user interface, at least when compared with plain
HTML-based applications. However, if you have used more advanced Single
Page Applications you have probably seen the pattern where an email
field (or a similar sort of input) is validated <em class="test">as you
type</em>.</p>
<p>This seems like the sort of interactivity that is only possible with
a sophisticated, complex JavaScript framework, right?</p>
<p>Well, no.</p>
<p>It turns out that you can implement this functionality in htmx, using
pure HTML attributes.</p>
<p>In fact, all we need to do is to change our trigger. Currently, we
are using the default trigger for inputs, which is the
<code>change</code> event. To validate as the user types, we would want
to capture the <code>keyup</code> event as well:</p>
<figure>
<div class="sourceCode" id="cb23"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb23-1"><a aria-hidden="true" href="#cb23-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb23-2"><a aria-hidden="true" href="#cb23-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb23-3"><a aria-hidden="true" href="#cb23-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"email"</span></span>
<span id="cb23-4"><a aria-hidden="true" href="#cb23-4" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/email"</span></span>
<span id="cb23-5"><a aria-hidden="true" href="#cb23-5" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"next .error"</span></span>
<span id="cb23-6"><a aria-hidden="true" href="#cb23-6" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"change, keyup"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb23-7"><a aria-hidden="true" href="#cb23-7" tabindex="-1"></a> placeholder="Email" value="{{ contact.email }}"&gt;</span>
<span id="cb23-8"><a aria-hidden="true" href="#cb23-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb23-9"><a aria-hidden="true" href="#cb23-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Triggering With <code>keyup</code>
events</p></figcaption>
</figure>
<ol>
<li><p>An explicit <code>keyup</code> trigger has been added along with
<code>change</code>.</p></li>
</ol>
<p>With this tiny change, every time a user types a character we will
issue a request and validate the email. Simple.</p>
<h3 id="_debouncing_our_validation_requests">Debouncing Our Validation
Requests</h3>
<p>Simple, yes, but probably not what we want: issuing a new request on
every key up event would be very wasteful and could potentially
overwhelm your server. What we want instead is only issue the request if
the user has paused for a small amount of time. This is called
“debouncing” the input, where requests are delayed until things have
“settled down”.</p>
<p>Htmx supports a <code>delay</code> modifier for triggers that allows
you to debounce a request by adding a delay before the request is sent.
If another event of the same kind appears within that interval, htmx
will not issue the request and will reset the timer.</p>
<p>This turns out to be exactly what we want for our email input: if the
user is busy typing in an email we won’t interrupt them, but as soon as
they pause or leave the field, we’ll issue a request.</p>
<p>Let’s add a delay of 200 milliseconds to the <code>keyup</code>
trigger, which is long enough to detect that the user has stopped
typing.:</p>
<figure>
<div class="sourceCode" id="cb24"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb24-1"><a aria-hidden="true" href="#cb24-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb24-2"><a aria-hidden="true" href="#cb24-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb24-3"><a aria-hidden="true" href="#cb24-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"email"</span></span>
<span id="cb24-4"><a aria-hidden="true" href="#cb24-4" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/email"</span></span>
<span id="cb24-5"><a aria-hidden="true" href="#cb24-5" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"next .error"</span></span>
<span id="cb24-6"><a aria-hidden="true" href="#cb24-6" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"change, keyup delay:200ms"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb24-7"><a aria-hidden="true" href="#cb24-7" tabindex="-1"></a> placeholder="Email" value="{{ contact.email }}"&gt;</span>
<span id="cb24-8"><a aria-hidden="true" href="#cb24-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb24-9"><a aria-hidden="true" href="#cb24-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Debouncing the <code>keyup</code> event</p></figcaption>
</figure>
<ol>
<li><p>We debounce the <code>keyup</code> event by adding a
<code>delay</code> modifier.</p></li>
</ol>
<p>Now we no longer issue a stream of validation requests as the user
types. Instead, we wait until the user pauses for a bit and then issue
the request. Much better for our server, and still a great user
experience.</p>
<h3 id="_ignoring_non_mutating_keys">Ignoring Non-Mutating Keys</h3>
<p>There is one last issue we should address with the keyup event: as it
stands we will issue a request no matter <em class="test">which</em> keys are
pressed, even if they are keys that have no effect on the value of the
input, such as arrow keys. It would be better if there were a way to
only issue a request if the input value has changed.</p>
<p>And it turns out that htmx has support for that exact pattern, by
using the <code>changed</code> modifier for events. (Not to be confused
with the <code>change</code> event triggered by the DOM on input
elements.)</p>
<p>By adding <code>changed</code> to our <code>keyup</code> trigger, the
input will not issue validation requests unless the keyup event actually
updates the inputs value:</p>
<figure>
<div class="sourceCode" id="cb25"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb25-1"><a aria-hidden="true" href="#cb25-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb25-2"><a aria-hidden="true" href="#cb25-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"email"</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb25-3"><a aria-hidden="true" href="#cb25-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> name</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> id</span><span class="op">=</span><span class="st">"email"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"email"</span></span>
<span id="cb25-4"><a aria-hidden="true" href="#cb25-4" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/email"</span></span>
<span id="cb25-5"><a aria-hidden="true" href="#cb25-5" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"next .error"</span></span>
<span id="cb25-6"><a aria-hidden="true" href="#cb25-6" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"change, keyup delay:200ms changed"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb25-7"><a aria-hidden="true" href="#cb25-7" tabindex="-1"></a> placeholder="Email" value="{{ contact.email }}"&gt;</span>
<span id="cb25-8"><a aria-hidden="true" href="#cb25-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> class</span><span class="op">=</span><span class="st">"error"</span><span class="dt">&gt;</span>{{ contact.errors['email'] }}<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb25-9"><a aria-hidden="true" href="#cb25-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Only sending requests when the input value
changes</p></figcaption>
</figure>
<ol>
<li><p>We do away with pointless requests by only issuing them when the
input’s value has actually changed.</p></li>
</ol>
<p>That’s some pretty good-looking and powerful HTML, providing an
experience that most developers would think requires a complicated
client-side solution.</p>
<p>With a total of three attributes and a simple new server-side
endpoint, we have added a fairly sophisticated user experience to our
web application. Even better, any email validation rules we add on the
server side will <em class="test">automatically</em> just work using this model:
because we are using hypermedia as our communication mechanism there is
no need to keep a client-side and server-side model in sync with one
another.</p>
<p>A great demonstration of the power of the hypermedia
architecture!</p>
<h2 id="_another_application_improvement_paging">Another Application
Improvement: Paging</h2>
<p>Let’s move on from the contact editing page for a bit and improve the
root page of the application, found at the <code>/contacts</code> path
and rendering the <code>index.html</code> template.</p>
<p>Currently, Contact.app does not support paging: if there are 10,000
contacts in the database we will show all 10,000 contacts on the root
page. Showing so much data can bog a browser (and a server) down, so
most web applications adopt a concept of “paging” to deal with data sets
this large, where only one “page” of a smaller number of items is shown,
with the ability to navigate around the pages in the data set.</p>
<p>Let’s fix our application so that we only show ten contacts at a time
with a “Next” and “Previous” link if there are more than 10 contacts in
the contact database.</p>
<p>The first change we will make is to add a simple paging widget to our
<code>index.html</code> template.</p>
<p>We will conditionally include two links:</p>
<ul>
<li><p>If we are beyond the “first” page, we will include a link to the
previous page</p></li>
<li><p>If there are ten contacts in the current result set, we will
include a link to the next page</p></li>
</ul>
<p>This isn’t a perfect paging widget: ideally we’d show the number of
pages and offer the ability to do more specific page navigation, and
there is the possibility that the next page might have 0 results in it
since we aren’t checking the total results count, but it will do for now
for our simple application.</p>
<p>Let’s look at the jinja template code for this in
<code>index.html</code>.</p>
<figure>
<div class="sourceCode" id="cb26"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb26-1"><a aria-hidden="true" href="#cb26-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb26-2"><a aria-hidden="true" href="#cb26-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> style</span><span class="op">=</span><span class="st">"float: right"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb26-3"><a aria-hidden="true" href="#cb26-3" tabindex="-1"></a> {% if page &gt; 1 %}</span>
<span id="cb26-4"><a aria-hidden="true" href="#cb26-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts?page={{ page - 1 }}"</span><span class="dt">&gt;</span>Previous<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb26-5"><a aria-hidden="true" href="#cb26-5" tabindex="-1"></a> {% endif %}</span>
<span id="cb26-6"><a aria-hidden="true" href="#cb26-6" tabindex="-1"></a> {% if contacts|length == 10 %}</span>
<span id="cb26-7"><a aria-hidden="true" href="#cb26-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts?page={{ page + 1 }}"</span><span class="dt">&gt;</span>Next<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb26-8"><a aria-hidden="true" href="#cb26-8" tabindex="-1"></a> {% endif %}</span>
<span id="cb26-9"><a aria-hidden="true" href="#cb26-9" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb26-10"><a aria-hidden="true" href="#cb26-10" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding paging widgets to our list of
contacts</p></figcaption>
</figure>
<ol>
<li><p>Include a new div under the table to hold our navigation
links.</p></li>
<li><p>If we are beyond page 1, include an anchor tag with the page
decremented by one.</p></li>
<li><p>If there are 10 contacts in the current page, include an anchor
tag linking to the next page by incrementing it by one.</p></li>
</ol>
<p>Note that here we are using a special jinja filter syntax
<code>contacts|length</code> to compute the length of the contacts list.
The details of this filter syntax is beyond the scope of this book, but
in this case you can think of it as invoking the
<code>contacts.length</code> property and then comparing that with
<code>10</code>.</p>
<p>Now that we have these links in place, let’s address the server-side
implementation of paging.</p>
<p>We are using the <code>page</code> request parameter to encode the
paging state of the UI. So, in our handler, we need to look for that
<code>page</code> parameter and pass that through to our model, as an
integer, so the model knows which page of contacts to return:</p>
<figure>
<div class="sourceCode" id="cb27"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb27-1"><a aria-hidden="true" href="#cb27-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb27-2"><a aria-hidden="true" href="#cb27-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb27-3"><a aria-hidden="true" href="#cb27-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>)</span>
<span id="cb27-4"><a aria-hidden="true" href="#cb27-4" tabindex="-1"></a> page <span class="op">=</span> <span class="bu">int</span>(request.args.get(<span class="st">"page"</span>, <span class="dv">1</span>)) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb27-5"><a aria-hidden="true" href="#cb27-5" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb27-6"><a aria-hidden="true" href="#cb27-6" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search)</span>
<span id="cb27-7"><a aria-hidden="true" href="#cb27-7" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb27-8"><a aria-hidden="true" href="#cb27-8" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>(page) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb27-9"><a aria-hidden="true" href="#cb27-9" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>,</span>
<span id="cb27-10"><a aria-hidden="true" href="#cb27-10" tabindex="-1"></a> contacts<span class="op">=</span>contacts_set, page<span class="op">=</span>page)</span></code></pre></div>
<figcaption><p>Adding paging to our request handler</p></figcaption>
</figure>
<ol>
<li><p>Resolve the page parameter, defaulting to page 1 if no page is
passed in.</p></li>
<li><p>Pass the page through to the model when loading all contacts so
it knows which page of 10 contacts to return.</p></li>
</ol>
<p>This is fairly straightforward: we just need to get another
parameter, like the <code>q</code> parameter we passed in for searching
contacts earlier, convert it to an integer and then pass it through to
the <code>Contact</code> model, so it knows which page to return.</p>
<p>And, with that small change, we are done: we now have a very basic
paging mechanism for our web application.</p>
<p>And, believe it or not, it is already using AJAX, thanks to our use
of <code>hx-boost</code> in the application. Easy!</p>
<h3 id="_click_to_load">Click To Load</h3>
<p>This paging mechanism is fine for a basic web application, and it is
used extensively on the internet. But it has some drawbacks associated
with it: every time you click the “Next” or “Previous” buttons you get a
whole new page of contacts and lose any context you had on the previous
page.</p>
<p>Sometimes a more advanced paging UI pattern might be better. Maybe,
rather than loading in a new page of elements and replacing the current
elements, it would be nicer to append the next page of elements
<em class="test">inline</em>, after the current elements.</p>
<p>This is the common “click to load” UX pattern, found in more advanced
web applications.</p>
<figure id="fig-clicktoload">
<p><img src="https://hypermedia.systems/images/screenshot_click_to_load.png"/></p>
<figcaption><p>A Click To Load UI</p></figcaption>
</figure>
<p>In <a class="ref" href="#fig-clicktoload">[fig-clicktoload]</a>, you
have a button that you can click, and it will load the next set of
contacts directly into the page, rather than “paging” to the next page.
This allows you to keep the current contacts “in context” visually on
the page, but still progress through them as you would in a normal,
paged user interface.</p>
<p>Let’s see how we can implement this UX pattern in htmx.</p>
<p>It’s actually surprisingly simple: we can just take the existing
“Next” link and repurpose it a bit using nothing but a few htmx
attributes!</p>
<p>We want to have a button that, when clicked, appends the rows from
the next page of contacts to the current, existing table, rather than
re-rendering the whole table. This can be achieved by adding a new row
to our table that has just such a button in it:</p>
<figure>
<div class="sourceCode" id="cb28"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb28-1"><a aria-hidden="true" href="#cb28-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb28-2"><a aria-hidden="true" href="#cb28-2" tabindex="-1"></a>{% for contact in contacts %}</span>
<span id="cb28-3"><a aria-hidden="true" href="#cb28-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb28-4"><a aria-hidden="true" href="#cb28-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.first }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb28-5"><a aria-hidden="true" href="#cb28-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.last }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb28-6"><a aria-hidden="true" href="#cb28-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.phone }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb28-7"><a aria-hidden="true" href="#cb28-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.email }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb28-8"><a aria-hidden="true" href="#cb28-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb28-9"><a aria-hidden="true" href="#cb28-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb28-10"><a aria-hidden="true" href="#cb28-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb28-11"><a aria-hidden="true" href="#cb28-11" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb28-12"><a aria-hidden="true" href="#cb28-12" tabindex="-1"></a>{% endfor %}</span>
<span id="cb28-13"><a aria-hidden="true" href="#cb28-13" tabindex="-1"></a>{% if contacts|length == 10 %} <span class="er">&lt;</span>1&gt;</span>
<span id="cb28-14"><a aria-hidden="true" href="#cb28-14" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb28-15"><a aria-hidden="true" href="#cb28-15" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="ot"> colspan</span><span class="op">=</span><span class="st">"5"</span><span class="ot"> style</span><span class="op">=</span><span class="st">"text-align: center"</span><span class="dt">&gt;</span></span>
<span id="cb28-16"><a aria-hidden="true" href="#cb28-16" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"closest tr"</span><span class="ot"> </span><span class="er">&lt;2</span><span class="dt">&gt;</span></span>
<span id="cb28-17"><a aria-hidden="true" href="#cb28-17" tabindex="-1"></a> hx-swap="outerHTML" <span class="er">&lt;</span>3&gt;</span>
<span id="cb28-18"><a aria-hidden="true" href="#cb28-18" tabindex="-1"></a> hx-select="tbody &gt; tr" <span class="er">&lt;</span>4&gt;</span>
<span id="cb28-19"><a aria-hidden="true" href="#cb28-19" tabindex="-1"></a> hx-get="/contacts?page={{ page + 1 }}"&gt;</span>
<span id="cb28-20"><a aria-hidden="true" href="#cb28-20" tabindex="-1"></a> Load More</span>
<span id="cb28-21"><a aria-hidden="true" href="#cb28-21" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb28-22"><a aria-hidden="true" href="#cb28-22" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb28-23"><a aria-hidden="true" href="#cb28-23" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb28-24"><a aria-hidden="true" href="#cb28-24" tabindex="-1"></a>{% endif %}</span>
<span id="cb28-25"><a aria-hidden="true" href="#cb28-25" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">tbody</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Changing to “click to load”</p></figcaption>
</figure>
<ol>
<li><p>Only show “Load More” if there are 10 contact results in the
current page.</p></li>
<li><p>Target the closest enclosing row.</p></li>
<li><p>Replace the entire row with the response from the
server.</p></li>
<li><p>Select out the table rows from the response.</p></li>
</ol>
<p>Let’s go through each attribute in detail here.</p>
<p>First, we are using <code>hx-target</code> to target the “closest”
<code>tr</code> element, that is, the closest <em class="test">parent</em> table
row.</p>
<p>Second, we want to replace this <em class="test">entire</em> row with whatever
content comes back from the server.</p>
<p>Third, we want to yank out only the <code>tr</code> elements in the
response. We are replacing this <code>tr</code> element with a new set
of <code>tr</code> elements, which will have additional contact
information in them, as well as, if necessary, a new “Load More” button
that points to the <em class="test">next</em> next page. To do this, we use a CSS
selector <code>tbody &gt; tr</code> to ensure we only pull out the rows
in the body of the table in the response. This avoids including rows in
the table header, for example.</p>
<p>Finally, we issue an HTTP <code>GET</code> to the url that will serve
the next page of contacts, which looks just like the “Next” link from
above.</p>
<p>Somewhat surprisingly, no server-side changes are necessary for this
new functionality. This is because of the flexibility that htmx gives
you with respect to how it processes server responses.</p>
<p>So, four attributes, and we now have a sophisticated “Click To Load”
UX, via htmx.</p>
<h3 id="_infinite_scroll">Infinite Scroll</h3>
<p>Another common pattern for dealing with large sets of things is known
as the “Infinite Scroll” pattern. In this pattern, as the last item of a
list or table of elements is scrolled into view, more elements are
loaded and appended to the list or table.</p>
<p>Now, this behavior makes more sense in situations where a user is
exploring a category or series of social media posts, rather than in the
context of a contact application. However, for completeness, and to just
show what you can do with htmx, we will implement this pattern as
well.</p>
<p>It turns out that we can repurpose the “Click To Load” code to
implement this new pattern quite easily: if you think about it for a
moment, infinite scroll is really just the “Click To Load” logic, but
rather than loading when a click event occurs, we want to load when an
element is “revealed” in the view portal of the browser.</p>
<p>As luck would have it, htmx offers a synthetic (non-standard) DOM
event, <code>revealed</code> that can be used in tandem with the
<code>hx-trigger</code> attribute, to trigger a request when, well, when
an element is revealed.</p>
<p>So let’s convert our button to a span and take advantage of this
event:</p>
<figure>
<div class="sourceCode" id="cb29"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb29-1"><a aria-hidden="true" href="#cb29-1" tabindex="-1"></a>{% if contacts|length == 10 %}</span>
<span id="cb29-2"><a aria-hidden="true" href="#cb29-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb29-3"><a aria-hidden="true" href="#cb29-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="ot"> colspan</span><span class="op">=</span><span class="st">"5"</span><span class="ot"> style</span><span class="op">=</span><span class="st">"text-align: center"</span><span class="dt">&gt;</span></span>
<span id="cb29-4"><a aria-hidden="true" href="#cb29-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"closest tr"</span></span>
<span id="cb29-5"><a aria-hidden="true" href="#cb29-5" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"revealed"</span></span>
<span id="cb29-6"><a aria-hidden="true" href="#cb29-6" tabindex="-1"></a><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span></span>
<span id="cb29-7"><a aria-hidden="true" href="#cb29-7" tabindex="-1"></a><span class="ot"> hx-select</span><span class="op">=</span><span class="st">"tbody &gt; tr"</span></span>
<span id="cb29-8"><a aria-hidden="true" href="#cb29-8" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts?page={{ page + 1 }}"</span><span class="dt">&gt;</span>Loading More...<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb29-9"><a aria-hidden="true" href="#cb29-9" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb29-10"><a aria-hidden="true" href="#cb29-10" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb29-11"><a aria-hidden="true" href="#cb29-11" tabindex="-1"></a>{% endif %}</span></code></pre></div>
<figcaption><p>Changing to “infinite scroll”</p></figcaption>
</figure>
<ol>
<li><p>We have converted our element from a button to a span, since the
user will not be clicking on it.</p></li>
<li><p>We trigger the request when the element is revealed, that is when
it comes into view in the portal.</p></li>
</ol>
<p>All we needed to do to convert from “Click to Load” to “Infinite
Scroll” was to update our element to be a span and then add the
<code>revealed</code> event trigger.</p>
<p>The fact that switching to infinite scroll was so easy shows how well
htmx generalizes HTML: just a few attributes allow us to dramatically
expand what we can achieve in the hypermedia.</p>
<p>And, again, we are doing all this while taking advantage of the
RESTful model of the web. Despite all this new behavior, we are still
exchanging hypermedia with the server, with no JSON API response to be
seen.</p>
<p>As the web was designed.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Caution With Modals and “display:
none”</h2>
<p><em class="test">Think twice about modals.</em> Modal windows have become popular,
almost standard, in many web applications today.</p>
<p>Unfortunately, modal windows do not play well with much of the
infrastructure of the web and introduce client-side state that can be
difficult (though not impossible) to integrate cleanly with the
hypermedia-based approach.</p>
<p>Modal windows can be used safely for views that don’t constitute a
resource or correspond to a domain entity:</p>
<ul>
<li><p>Alerts</p></li>
<li><p>Confirmation dialogs</p></li>
<li><p>Forms for creating/updating entities</p></li>
</ul>
<p>Otherwise, consider using alternatives such as inline editing, or a
separate page, rather than a modal.</p>
<p><em class="test">Use <code>display: none;</code> with care</em>. The issue is that
it is not purely cosmetic — it also removes elements from the
accessibility tree and keyboard focus. This is sometimes done to present
the same content to visual and aural interfaces. If you want to hide an
element visually without hiding it from assistive technology (e.g. the
element contains information that is communicated through styling), you
can use this utility class:</p>
<figure>
<div class="sourceCode" id="cb30"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb30-1"><a aria-hidden="true" href="#cb30-1" tabindex="-1"></a><span class="fu">.vh</span> {</span>
<span id="cb30-2"><a aria-hidden="true" href="#cb30-2" tabindex="-1"></a> <span class="kw">position</span><span class="ch">:</span> <span class="dv">absolute</span><span class="op">;</span></span>
<span id="cb30-3"><a aria-hidden="true" href="#cb30-3" tabindex="-1"></a> clip<span class="ch">:</span> rect<span class="fu">(</span><span class="dv">0</span> <span class="dv">0</span> <span class="dv">0</span> <span class="dv">0</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb30-4"><a aria-hidden="true" href="#cb30-4" tabindex="-1"></a> <span class="kw">clip-path</span><span class="ch">:</span> <span class="fu">inset(</span><span class="dv">50</span><span class="dt">%</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb30-5"><a aria-hidden="true" href="#cb30-5" tabindex="-1"></a> <span class="kw">block-size</span><span class="ch">:</span> <span class="dv">1</span><span class="dt">px</span><span class="op">;</span></span>
<span id="cb30-6"><a aria-hidden="true" href="#cb30-6" tabindex="-1"></a> <span class="kw">inline-size</span><span class="ch">:</span> <span class="dv">1</span><span class="dt">px</span><span class="op">;</span></span>
<span id="cb30-7"><a aria-hidden="true" href="#cb30-7" tabindex="-1"></a> <span class="kw">overflow</span><span class="ch">:</span> <span class="dv">hidden</span><span class="op">;</span></span>
<span id="cb30-8"><a aria-hidden="true" href="#cb30-8" tabindex="-1"></a> <span class="kw">white-space</span><span class="ch">:</span> <span class="dv">nowrap</span><span class="op">;</span></span>
<span id="cb30-9"><a aria-hidden="true" href="#cb30-9" tabindex="-1"></a>}</span></code></pre></div>
</figure>
<p><code>vh</code> is short for “visually hidden.” This class uses
multiple methods and workarounds to make sure no browser removes the
element’s function.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">More Htmx Patterns</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_active_search">Active Search</a>
<ul>
<li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_our_current_search_ui">Our Current Search UI</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_adding_active_search">Adding Active Search</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_targeting_the_correct_element">Targeting The Correct
Element</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_paring_down_our_content">Paring Down Our Content</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_http_request_headers_in_htmx">HTTP Request Headers In Htmx</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_factoring_your_templates">Factoring Your Templates</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_using_our_new_template">Using Our New Template</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_updating_the_navigation_bar_with_hx_push_url">Updating the
Navigation Bar With “hx-push-url”</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_adding_a_request_indicator">Adding A Request Indicator</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_lazy_loading">Lazy Loading</a>
<ul>
<li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_pulling_out_the_expensive_code">Pulling Out The Expensive
Code</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_adding_an_indicator">Adding An Indicator</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_but_thats_not_lazy">But That’s Not Lazy!</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_inline_delete">Inline Delete</a>
<ul>
<li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_narrowing_our_target">Narrowing Our Target</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_updating_the_server_side">Updating The Server Side</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_the_htmx_swapping_model">The Htmx Swapping Model</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_taking_advantage_of_htmx_swapping">Taking Advantage of
“htmx-swapping”</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_bulk_delete">Bulk Delete</a>
<ul>
<li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_the_delete_selected_contacts_button">The “Delete Selected
Contacts” Button</a>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#_the_server_side_for_delete_selected_contacts">The Server Side
for Delete Selected Contacts</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/more-htmx-patterns/#html-note-title">HTML Notes: Accessible by Default?</a>
</li></ul>
</details>
<div class="division-content">
<h2 id="_active_search">Active Search</h2>
<p>So far so good with Contact.app: we have a nice little web
application with some significant improvements over a plain HTML-based
application. We’ve added a proper “Delete Contact” button, done some
dynamic validation of input and looked at different approaches to add
paging to the application. As we have said, many web developers would
expect that a lot of JavaScript-based scripting would be required to get
these features, but we’ve done it all in relatively pure HTML, using
only htmx attributes.</p>
<p>We <em class="test">will</em> eventually add some client-side scripting to our
application: hypermedia is powerful, but it isn’t <em class="test">all powerful</em>
and sometimes scripting might be the best (or only) way to achieve a
given goal. For now, however, let’s see what we can accomplish with
hypermedia.</p>
<p>The first advanced htmx feature we will create is known as the
“Active Search” pattern. Active Search is when, as a user types text
into a search box, the results of that search are dynamically shown.
This pattern was made popular when Google adopted it for search results,
and many applications now implement it.</p>
<p>To implement Active Search, we are going to use techniques closely
related to the way we did email validation in the previous chapter. If
you think about it, the two features are similar in many ways: in both
cases we want to issue a request as the user types into an input and
then update some other element with a response. The server-side
implementations will, of course, be very different, but the frontend
code will look fairly similar due to htmx’s general approach of “issue a
request on an event and replace something on the screen.”</p>
<h3 id="_our_current_search_ui">Our Current Search UI</h3>
<p>Let’s recall what the search field in our application currently looks
like:</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"get"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"tool-bar"</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"search"</span><span class="dt">&gt;</span>Search Term<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb1-4"><a aria-hidden="true" href="#cb1-4" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb1-5"><a aria-hidden="true" href="#cb1-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"submit"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"Search"</span><span class="dt">/&gt;</span></span>
<span id="cb1-6"><a aria-hidden="true" href="#cb1-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our search form</p></figcaption>
</figure>
<ol>
<li><p>The <code>q</code> or “query” parameter our client-side code uses
to search.</p></li>
</ol>
<p>Recall that we have some server-side code that looks for the
<code>q</code> parameter and, if it is present, searches the contacts
for that term.</p>
<p>As it stands right now, the user must hit enter when the search input
is focused, or click the “Search” button. Both of these events will
trigger a <code>submit</code> event on the form, causing it to issue an
HTTP <code>GET</code> and re-rendering the whole page.</p>
<p>Currently, thanks to <code>hx-boost</code>, the form will use an AJAX
request for this <code>GET</code>, but we don’t yet get that nice
search-as-you-type behavior we want.</p>
<h3 id="_adding_active_search">Adding Active Search</h3>
<p>To add active search behavior, we will attach a few htmx attributes
to the search input. We will leave the current form as it is, with an
<code>action</code> and <code>method</code>, so that the normal search
behavior works even if a user does not have JavaScript enabled. This
will make our “Active Search” improvement a nice “progressive
enhancement.”</p>
<p>So, in addition to the regular form behavior, we <em class="test">also</em> want
to issue an HTTP <code>GET</code> request when a key up occurs. We want
to issue this request to the same URL as the normal form submission.
Finally, we only want to do this after a small pause in typing has
occurred.</p>
<p>As we said, this functionality is very similar to what we needed for
email validation. In fact, we can copy the <code>hx-trigger</code>
attribute directly from our email validation example, with its small
200-millisecond delay, to wait for a user to stop typing before a
request is triggered.</p>
<p>This is another example of how common patterns come up again and
again when using htmx.</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"get"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"tool-bar"</span><span class="dt">&gt;</span></span>
<span id="cb2-2"><a aria-hidden="true" href="#cb2-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"search"</span><span class="dt">&gt;</span>Search Term<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb2-3"><a aria-hidden="true" href="#cb2-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb2-4"><a aria-hidden="true" href="#cb2-4" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb2-5"><a aria-hidden="true" href="#cb2-5" tabindex="-1"></a> hx-get="/contacts" <span class="er">&lt;</span>2&gt;</span>
<span id="cb2-6"><a aria-hidden="true" href="#cb2-6" tabindex="-1"></a> hx-trigger="search, keyup delay:200ms changed"/&gt; <span class="er">&lt;</span>3&gt;</span>
<span id="cb2-7"><a aria-hidden="true" href="#cb2-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"submit"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"Search"</span><span class="dt">/&gt;</span></span>
<span id="cb2-8"><a aria-hidden="true" href="#cb2-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding active search behavior</p></figcaption>
</figure>
<ol>
<li><p>Keep the original attributes, so search will work if JavaScript
is not available.</p></li>
<li><p>Issue a <code>GET</code> to the same URL as the form.</p></li>
<li><p>Nearly the same <code>hx-trigger</code> specification as for the
email input validation.</p></li>
</ol>
<p>We made a small change to the <code>hx-trigger</code> attribute: we
switched out the <code>change</code> event for the <code>search</code>
event. The <code>search</code> event is triggered when someone clears
the search or hits the enter key. It is a non-standard event, but it
doesn’t hurt to include here. The main functionality of the feature is
provided by the second triggering event, the <code>keyup</code>. As in
the email example, this trigger is delayed with the
<code>delay:200ms</code> modifier to “debounce” the input requests and
avoid hammering our server with requests on every keyup.</p>
<h3 id="_targeting_the_correct_element">Targeting The Correct
Element</h3>
<p>What we have is close to what we want, but we need to set up the
correct target. Recall that the default target for an element is itself.
As things currently stand, an HTTP <code>GET</code> request will be
issued to the <code>/contacts</code> path, which will, as of now, return
an entire HTML document of search results, and then this whole document
will be inserted into the <em class="test">inner</em> HTML of the search input.</p>
<p>This is, in fact, nonsense: <code>input</code> elements aren’t
allowed to have any HTML inside of them. The browser will, sensibly,
just ignore the htmx request to put the response HTML inside the input.
So, at this point, when a user types anything into our input, a request
will be issued (you can see it in your browser development console if
you try it out) but, unfortunately, it will appear to the user as if
nothing has happened at all.</p>
<p>To fix this issue, what do we want to target with the update instead?
Ideally we’d like to just target the actual results: there is no reason
to update the header or search input, and that could cause an annoying
flash as focus jumps around.</p>
<p>The <code>hx-target</code> attribute allows us to do exactly that.
Let’s use it to target the results body, the <code>tbody</code> element
in the table of contacts:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"get"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"tool-bar"</span><span class="dt">&gt;</span></span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">"search"</span><span class="dt">&gt;</span>Search Term<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span></span>
<span id="cb3-5"><a aria-hidden="true" href="#cb3-5" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb3-6"><a aria-hidden="true" href="#cb3-6" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"search, keyup delay:200ms changed"</span></span>
<span id="cb3-7"><a aria-hidden="true" href="#cb3-7" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"tbody"</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb3-8"><a aria-hidden="true" href="#cb3-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"submit"</span><span class="ot"> value</span><span class="op">=</span><span class="st">"Search"</span><span class="dt">/&gt;</span></span>
<span id="cb3-9"><a aria-hidden="true" href="#cb3-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span>
<span id="cb3-10"><a aria-hidden="true" href="#cb3-10" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">table</span><span class="dt">&gt;</span></span>
<span id="cb3-11"><a aria-hidden="true" href="#cb3-11" tabindex="-1"></a> ...</span>
<span id="cb3-12"><a aria-hidden="true" href="#cb3-12" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb3-13"><a aria-hidden="true" href="#cb3-13" tabindex="-1"></a> ...</span>
<span id="cb3-14"><a aria-hidden="true" href="#cb3-14" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb3-15"><a aria-hidden="true" href="#cb3-15" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding active search behavior</p></figcaption>
</figure>
<ol>
<li><p>Target the <code>tbody</code> tag on the page.</p></li>
</ol>
<p>Because there is only one <code>tbody</code> on the page, we can use
the general CSS selector <code>tbody</code> and htmx will target the
body of the table on the page.</p>
<p>Now if you try typing something into the search box, we’ll see some
results: a request is made and the results are inserted into the
document within the <code>tbody</code>. Unfortunately, the content that
is coming back is still an entire HTML document.</p>
<p>Here we end up with a “double render” situation, where an entire
document has been inserted <em class="test">inside</em> another element, with all the
navigation, headers and footers and so forth re-rendered within that
element. This is an example of one of those mis-targeting issues we
mentioned earlier.</p>
<p>Thankfully, it is pretty easy to fix.</p>
<h3 id="_paring_down_our_content">Paring Down Our Content</h3>
<p>Now, we could use the same trick we reached for in the “Click To
Load” and “Infinite Scroll” features: the <code>hx-select</code>
attribute. Recall that the <code>hx-select</code> attribute allows us to
pick out the part of the response we are interested in using a CSS
selector.</p>
<p>So we could add this to our input:</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span></span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb4-4"><a aria-hidden="true" href="#cb4-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"change, keyup delay:200ms changed"</span></span>
<span id="cb4-5"><a aria-hidden="true" href="#cb4-5" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"tbody"</span></span>
<span id="cb4-6"><a aria-hidden="true" href="#cb4-6" tabindex="-1"></a><span class="ot"> hx-select</span><span class="op">=</span><span class="st">"tbody tr"</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
<figcaption><p>Using “hx-select” for active search</p></figcaption>
</figure>
<ol>
<li><p>Adding an <code>hx-select</code> that picks out the table rows in
the <code>tbody</code> of the response.</p></li>
</ol>
<p>However, that isn’t the only fix for this problem, and, in this case,
it isn’t the most efficient one. Instead, let’s change the
<em class="test">server-side</em> of our Hypermedia-Driven Application to serve
<em class="test">only the HTML content needed</em>.</p>
<h3 id="_http_request_headers_in_htmx">HTTP Request Headers In Htmx</h3>
<p>In this section, we’ll look at another, more advanced technique for
dealing with a situation where we only want a <em class="test">partial bit</em> of
HTML, rather than a full document. Currently, we are letting the server
create the full HTML document as response and then, on the client side,
we filter the HTML down to the bits that we want. This is easy to do,
and, in fact, might be necessary if we don’t control the server side or
can’t easily modify responses.</p>
<p>In our application, however, since we are doing “Full Stack”
development (that is: we control both frontend <em class="test">and</em> backend
code, and can easily modify either) we have another option: we can
modify our server responses to return only the content necessary, and
remove the need to do client-side filtering.</p>
<p>This turns out to be more efficient, since we aren’t returning all
the content surrounding the bit we are interested in, saving bandwidth
as well as CPU and memory on the server side. So let’s explore returning
different HTML content based on the context information that htmx
provides with the HTTP requests it makes.</p>
<p>Here’s a look again at the current server-side code for our search
logic:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>)</span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb5-6"><a aria-hidden="true" href="#cb5-6" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb5-7"><a aria-hidden="true" href="#cb5-7" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>()</span>
<span id="cb5-8"><a aria-hidden="true" href="#cb5-8" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>, contacts<span class="op">=</span>contacts_set) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Server-side search</p></figcaption>
</figure>
<ol>
<li><p>This is where the search logic happens.</p></li>
<li><p>We simply re-render the <code>index.html</code> template every
time, no matter what.</p></li>
</ol>
<p>How do we want to change this? We want to render two different bits
of HTML content <em class="test">conditionally</em>:</p>
<ul>
<li><p>If this is a “normal” request for the entire page, we want to
render the <code>index.html</code> template in the current manner. In
fact, we don’t want anything to change if this is a “normal”
request.</p></li>
<li><p>However, if this is an “Active Search” request, we only want to
render the content that is within the <code>tbody</code>, that is, just
the table rows of the page.</p></li>
</ul>
<p>So we need some way to determine exactly which of these two different
types of requests to the <code>/contact</code> URL is being made, in
order to know exactly which content we want to render.</p>
<p>It turns out that htmx helps us distinguish between these two cases
by including a number of HTTP <em class="test">Request Headers</em> when it makes
requests. Request Headers are a feature of HTTP, allowing clients (e.g.,
web browsers) to include name/value pairs of metadata associated with
requests to help the server understand what the client is
requesting.</p>
<p>Here is an example of (some of) the headers the FireFox browser
issues when requesting <code>https://hypermedia.systems</code>:</p>
<figure>
<pre class="http"><code>GET / HTTP/2
Host: hypermedia.systems
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:103.0) Gecko/20100101 Firefox/103.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Cache-Control: no-cache
Connection: keep-alive
DNT: 1
Pragma: no-cache
</code></pre>
<figcaption><p>HTTP headers</p></figcaption>
</figure>
<p>Htmx takes advantage of this feature of HTTP and adds additional
headers and, therefore, additional <em class="test">context</em> to the HTTP requests
that it makes. This allows you to inspect those headers and choose what
logic to execute on the server, and what sort of HTML response you want
to send to the client.</p>
<p>Here is a table of the HTTP headers that htmx includes in HTTP
requests:</p>
<dl>
<dt><code>HX-Boosted</code></dt>
<dd>
<p>This will be the string “true” if the request is made via an element
using hx-boost</p>
</dd>
<dt><code>HX-Current-URL</code></dt>
<dd>
<p>This will be the current URL of the browser</p>
</dd>
<dt><code>HX-History-Restore-Request</code></dt>
<dd>
<p>This will be the string “true” if the request is for history
restoration after a miss in the local history cache</p>
</dd>
<dt><code>HX-Prompt</code></dt>
<dd>
<p>This will contain the user response to an hx-prompt</p>
</dd>
<dt><code>HX-Request</code></dt>
<dd>
<p>This value is always “true” for htmx-based requests</p>
</dd>
<dt><code>HX-Target</code></dt>
<dd>
<p>This value will be the id of the target element if it exists</p>
</dd>
<dt><code>HX-Trigger-Name</code></dt>
<dd>
<p>This value will be the name of the triggered element if it exists</p>
</dd>
<dt><code>HX-Trigger</code></dt>
<dd>
<p>This value will be the id of the triggered element if it exists</p>
</dd>
</dl>
<p>Looking through this list of headers, the last one stands out: we
have an id, <code>search</code> on our search input. So the value of the
<code>HX-Trigger</code> header should be set to <code>search</code> when
the request is coming from the search input, which has the id
<code>search</code>.</p>
<p>Let’s add some conditional logic to our controller to look for that
header and, if the value is <code>search</code>, we render only the rows
rather than the whole <code>index.html</code> template:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>)</span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb7-5"><a aria-hidden="true" href="#cb7-5" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search)</span>
<span id="cb7-6"><a aria-hidden="true" href="#cb7-6" tabindex="-1"></a> <span class="cf">if</span> request.headers.get(<span class="st">'HX-Trigger'</span>) <span class="op">==</span> <span class="st">'search'</span>: <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb7-7"><a aria-hidden="true" href="#cb7-7" tabindex="-1"></a> <span class="co"># </span><span class="al">TODO</span><span class="co">: render only the rows here &lt;2&gt;</span></span>
<span id="cb7-8"><a aria-hidden="true" href="#cb7-8" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb7-9"><a aria-hidden="true" href="#cb7-9" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>()</span>
<span id="cb7-10"><a aria-hidden="true" href="#cb7-10" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>, contacts<span class="op">=</span>contacts_set)</span></code></pre></div>
<figcaption><p>Updating our server-side search</p></figcaption>
</figure>
<ol>
<li><p>If the request header <code>HX-Trigger</code> is equal to
“search” we want to do something different.</p></li>
<li><p>We need to learn how to render just the table rows.</p></li>
</ol>
<p>OK, so how do we render only the result rows?</p>
<h3 id="_factoring_your_templates">Factoring Your Templates</h3>
<p>Now we come to a common pattern in htmx: we want to <em class="test">factor</em>
our server-side templates. This means that we want to break our
templates up a bit so that they can be called from multiple contexts. In
this case, we want to break the rows of the results table out to a
separate template we will call <code>rows.html</code>. We will include
it from the original <code>index.html</code> template, and also use it
in our controller to render it by itself when we want to respond with
only the rows for Active Search requests.</p>
<p>Here’s what the table in our <code>index.html</code> file currently
looks like:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">table</span><span class="dt">&gt;</span></span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb8-4"><a aria-hidden="true" href="#cb8-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>First <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Last <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Phone <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Email <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">/&gt;</span></span>
<span id="cb8-5"><a aria-hidden="true" href="#cb8-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb8-6"><a aria-hidden="true" href="#cb8-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span id="cb8-7"><a aria-hidden="true" href="#cb8-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb8-8"><a aria-hidden="true" href="#cb8-8" tabindex="-1"></a> {% for contact in contacts %}</span>
<span id="cb8-9"><a aria-hidden="true" href="#cb8-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb8-10"><a aria-hidden="true" href="#cb8-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.first }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb8-11"><a aria-hidden="true" href="#cb8-11" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.last }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb8-12"><a aria-hidden="true" href="#cb8-12" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.phone }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb8-13"><a aria-hidden="true" href="#cb8-13" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.email }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb8-14"><a aria-hidden="true" href="#cb8-14" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb8-15"><a aria-hidden="true" href="#cb8-15" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb8-16"><a aria-hidden="true" href="#cb8-16" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb8-17"><a aria-hidden="true" href="#cb8-17" tabindex="-1"></a> {% endfor %}</span>
<span id="cb8-18"><a aria-hidden="true" href="#cb8-18" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb8-19"><a aria-hidden="true" href="#cb8-19" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The contacts table</p></figcaption>
</figure>
<p>The <code>for</code> loop in this template is what produces all the
rows in the final content generated by <code>index.html</code>. What we
want to do is to move the <code>for</code> loop and, therefore, the rows
it creates out to a <em class="test">separate template file</em> so that only that
small bit of HTML can be rendered independently from
<code>index.html</code>.</p>
<p>Again, let’s call this new template <code>rows.html</code>:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a>{% for contact in contacts %}</span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.first }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb9-4"><a aria-hidden="true" href="#cb9-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.last }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb9-5"><a aria-hidden="true" href="#cb9-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.phone }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb9-6"><a aria-hidden="true" href="#cb9-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.email }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb9-7"><a aria-hidden="true" href="#cb9-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb9-8"><a aria-hidden="true" href="#cb9-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb9-9"><a aria-hidden="true" href="#cb9-9" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb9-10"><a aria-hidden="true" href="#cb9-10" tabindex="-1"></a>{% endfor %}</span></code></pre></div>
<figcaption><p>Our new <code>rows.html</code> file</p></figcaption>
</figure>
<p>Using this template we can render only the <code>tr</code> elements
for a given collection of contacts.</p>
<p>Of course, we still want to include this content in the
<code>index.html</code> template: we are <em class="test">sometimes</em> going to be
rendering the entire page, and sometimes only rendering the rows. In
order to keep the <code>index.html</code> template rendering properly,
we can include the <code>rows.html</code> template by using the jinja
<code>include</code> directive at the position we want the content from
<code>rows.html</code> inserted:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">table</span><span class="dt">&gt;</span></span>
<span id="cb10-2"><a aria-hidden="true" href="#cb10-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span id="cb10-3"><a aria-hidden="true" href="#cb10-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb10-4"><a aria-hidden="true" href="#cb10-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>First<span class="dt">&lt;/</span><span class="kw">th</span><span class="dt">&gt;</span></span>
<span id="cb10-5"><a aria-hidden="true" href="#cb10-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Last<span class="dt">&lt;/</span><span class="kw">th</span><span class="dt">&gt;</span></span>
<span id="cb10-6"><a aria-hidden="true" href="#cb10-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Phone<span class="dt">&lt;/</span><span class="kw">th</span><span class="dt">&gt;</span></span>
<span id="cb10-7"><a aria-hidden="true" href="#cb10-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Email<span class="dt">&lt;/</span><span class="kw">th</span><span class="dt">&gt;</span></span>
<span id="cb10-8"><a aria-hidden="true" href="#cb10-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;&lt;/</span><span class="kw">th</span><span class="dt">&gt;</span></span>
<span id="cb10-9"><a aria-hidden="true" href="#cb10-9" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb10-10"><a aria-hidden="true" href="#cb10-10" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span id="cb10-11"><a aria-hidden="true" href="#cb10-11" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb10-12"><a aria-hidden="true" href="#cb10-12" tabindex="-1"></a> {% include 'rows.html' %} <span class="er">&lt;</span>1&gt;</span>
<span id="cb10-13"><a aria-hidden="true" href="#cb10-13" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span id="cb10-14"><a aria-hidden="true" href="#cb10-14" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Including the new file</p></figcaption>
</figure>
<ol>
<li><p>This directive “includes” the <code>rows.html</code> file,
inserting its content into the current template.</p></li>
</ol>
<p>So far, so good: our <code>/contacts</code> page is still rendering
properly, just as it did before we split the rows out of the
<code>index.html</code> template.</p>
<h3 id="_using_our_new_template">Using Our New Template</h3>
<p>The last step in factoring our templates is to modify our web
controller to take advantage of the new <code>rows.html</code> template
file when it responds to an active search request.</p>
<p>Since <code>rows.html</code> is just another template, just like
<code>index.html</code>, all we need to do is call the
<code>render_template</code> function with <code>rows.html</code> rather
than <code>index.html</code>. This will render <em class="test">only</em> the row
content rather than the entire page:</p>
<figure>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>)</span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search)</span>
<span id="cb11-6"><a aria-hidden="true" href="#cb11-6" tabindex="-1"></a> <span class="cf">if</span> request.headers.get(<span class="st">'HX-Trigger'</span>) <span class="op">==</span> <span class="st">'search'</span>:</span>
<span id="cb11-7"><a aria-hidden="true" href="#cb11-7" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"rows.html"</span>, contacts<span class="op">=</span>contacts_set) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb11-8"><a aria-hidden="true" href="#cb11-8" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb11-9"><a aria-hidden="true" href="#cb11-9" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>()</span>
<span id="cb11-10"><a aria-hidden="true" href="#cb11-10" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>, contacts<span class="op">=</span>contacts_set)</span></code></pre></div>
<figcaption><p>Updating our server-side search</p></figcaption>
</figure>
<ol>
<li><p>Render the new template in the case of an active search.</p></li>
</ol>
<p>Now, when an Active Search request is made, rather than getting an
entire HTML document back, we only get a partial bit of HTML, the table
rows for the contacts that match the search. These rows are then
inserted into the <code>tbody</code> on the index page, without any need
for <code>hx-select</code> or other client-side processing.</p>
<p>And, as a bonus, the old form-based search <em class="test">still works</em>. We
conditionally render the rows only when the <code>search</code> input
issues the HTTP request via htmx. Again, this is a progressive
enhancement to our application.</p>
<div id="sidebar">
<div>
<div>
<p><strong>HTTP Headers &amp; Caching</strong></p>
</div>
<div>
<p>One subtle aspect of the approach we are taking here, using headers
to determine the content of what we return, is a feature baked into
HTTP: caching. In our request handler, we are now returning different
content depending on the value of the <code>HX-Trigger</code> header. If
we were to use HTTP Caching, we might get into a situation where someone
makes a <em class="test">non-htmx</em> request (e.g., refreshing a page) and yet the
<em class="test">htmx</em> content is returned from the HTTP cache, resulting in a
partial page of content for the user.</p>
<p>The solution to this problem is to use the HTTP Response
<code>Vary</code> header and call out the htmx headers that you are
using to determine what content you are returning. A full explanation of
HTTP Caching is beyond the scope of this book, but the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching">MDN
article on the topic</a> is quite good, and the <a href="https://htmx.org/docs/#caching">htmx documentation</a> discusses
this issue as well.</p>
</div>
</div>
</div>
<h3 id="_updating_the_navigation_bar_with_hx_push_url">Updating the
Navigation Bar With “hx-push-url”</h3>
<p>One shortcoming of our current Active Search implementation, when
compared with the normal form submission, is that when you submit the
form version it updates the navigation bar of the browser to include the
search term. So, for example, if you search for “joe” in the search box,
you will end up with a url that looks like this in your browser’s nav
bar:</p>
<figure>
<pre><code>https://example.com/contacts?q=joe
</code></pre>
<figcaption><p>The updated location after a form search</p></figcaption>
</figure>
<p>This is a nice feature of browsers: it allows you to bookmark this
search or to copy the URL and send it to someone else. All they have to
do is to click on the link, and they will repeat the exact same search.
This is also tied in with the browser’s notion of history: if you click
the back button it will take you to the previous URL that you came from.
If you submit two searches and want to go back to the first one, you can
simply hit back and the browser will “return” to that search.</p>
<p>As it stands right now, during our Active Search, we are not updating
the browser’s navigation bar. So, users aren’t getting links that can be
copied and pasted, and you aren’t getting history entries either, which
means no back button support. Fortunately, we’ve already seen how to fix
this: with the <code>hx-push-url</code> attribute.</p>
<p>The <code>hx-push-url</code> attribute lets you tell htmx “Please
push the URL of this request into the browser’s navigation bar.” Push
might seem like an odd verb to use here, but that’s the term that the
underlying browser history API uses, which stems from the fact that it
models browser history as a “stack” of locations: when you go to a new
location, that location is “pushed” onto the stack of history elements,
and when you click “back”, that location is “popped” off the history
stack.</p>
<p>So, to get proper history support for our Active Search, all we need
to do is to set the <code>hx-push-url</code> attribute to
<code>true</code>.</p>
<figure>
<div class="sourceCode" id="cb13"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb13-1"><a aria-hidden="true" href="#cb13-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb13-2"><a aria-hidden="true" href="#cb13-2" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span></span>
<span id="cb13-3"><a aria-hidden="true" href="#cb13-3" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb13-4"><a aria-hidden="true" href="#cb13-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"change, keyup delay:200ms changed"</span></span>
<span id="cb13-5"><a aria-hidden="true" href="#cb13-5" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"tbody"</span></span>
<span id="cb13-6"><a aria-hidden="true" href="#cb13-6" tabindex="-1"></a><span class="ot"> hx-push-url</span><span class="op">=</span><span class="st">"true"</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
<figcaption><p>Updating the URL during active search</p></figcaption>
</figure>
<ol>
<li><p>By adding the <code>hx-push-url</code> attribute with the value
<code>true</code>, htmx will update the URL when it makes a
request.</p></li>
</ol>
<p>Now, as Active Search requests are sent, the URL in the browser’s
navigation bar is updated to have the proper query in it, just like when
the form is submitted.</p>
<p>You might not <em class="test">want</em> this behavior. You might feel it would be
confusing to users to see the navigation bar updated and have history
entries for every Active Search made, for example. Which is fine: you
can simply omit the <code>hx-push-url</code> attribute and it will go
back to the behavior you want. The goal with htmx is to be flexible
enough to achieve the UX that <em class="test">you</em> want, while staying within
the declarative HTML model.</p>
<h3 id="_adding_a_request_indicator">Adding A Request Indicator</h3>
<p>A final touch for our Active Search pattern is to add a request
indicator to let the user know that a search is in progress. As it
stands the user has no explicit signal that the active search
functionality is handling a request. If the search takes a bit, a user
may end up thinking that the feature isn’t working. By adding a request
indicator we let the user know that the hypermedia application is busy
and they should wait (hopefully not too long!) for the request to
complete.</p>
<p>Htmx provides support for request indicators via the
<code>hx-indicator</code> attribute. This attribute takes, you guessed
it, a CSS selector that points to the indicator for a given element. The
indicator can be anything, but it is typically some sort of animated
image, such as a gif or svg file, that spins or otherwise communicates
visually that “something is happening.”</p>
<p>Let’s add a spinner after our search input:</p>
<figure>
<div class="sourceCode" id="cb14"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb14-1"><a aria-hidden="true" href="#cb14-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb14-2"><a aria-hidden="true" href="#cb14-2" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span></span>
<span id="cb14-3"><a aria-hidden="true" href="#cb14-3" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb14-4"><a aria-hidden="true" href="#cb14-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"change, keyup delay:200ms changed"</span></span>
<span id="cb14-5"><a aria-hidden="true" href="#cb14-5" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"tbody"</span></span>
<span id="cb14-6"><a aria-hidden="true" href="#cb14-6" tabindex="-1"></a><span class="ot"> hx-push-url</span><span class="op">=</span><span class="st">"true"</span></span>
<span id="cb14-7"><a aria-hidden="true" href="#cb14-7" tabindex="-1"></a><span class="ot"> hx-indicator</span><span class="op">=</span><span class="st">"#spinner"</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb14-8"><a aria-hidden="true" href="#cb14-8" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">img</span><span class="ot"> id</span><span class="op">=</span><span class="st">"spinner"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"htmx-indicator"</span></span>
<span id="cb14-9"><a aria-hidden="true" href="#cb14-9" tabindex="-1"></a><span class="ot"> src</span><span class="op">=</span><span class="st">"/static/img/spinning-circles.svg"</span></span>
<span id="cb14-10"><a aria-hidden="true" href="#cb14-10" tabindex="-1"></a><span class="ot"> alt</span><span class="op">=</span><span class="st">"Request In Flight..."</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>2&gt;</span></code></pre></div>
<figcaption><p>Adding a request indicator to search</p></figcaption>
</figure>
<ol>
<li><p>The <code>hx-indicator</code> attribute points to the indicator
image after the input.</p></li>
<li><p>The indicator is a spinning circle svg file, and has the
<code>htmx-indicator</code> class on it.</p></li>
</ol>
<p>We have added the spinner right after the input. This visually
co-locates the request indicator with the element making the request,
and makes it easy for a user to see that something is in fact
happening.</p>
<p>It just works, but how does htmx make the spinner appear and
disappear? Note that the indicator <code>img</code> tag has the
<code>htmx-indicator</code> class on it. <code>htmx-indicator</code> is
a CSS class that is automatically injected into the page by htmx. This
class sets the default <code>opacity</code> of an element to
<code>0</code>, which hides the element from view, while at the same
time not disrupting the layout of the page.</p>
<p>When an htmx request is triggered that points to this indicator,
another class, <code>htmx-request</code> is added to the indicator which
transitions its opacity to 1. So you can use just about anything as an
indicator, and it will be hidden by default. Then, when a request is in
flight, it will be shown. This is all done via standard CSS classes,
allowing you to control the transitions and even the mechanism by which
the indicator is shown (e.g., you might use <code>display</code> rather
than <code>opacity</code>).</p>
<div id="sidebar">
<div>
<div>
<p><strong>Use Request Indicators!</strong></p>
</div>
<div>
<p>Request indicators are an important UX aspect of any distributed
application. It is unfortunate that browsers have de-emphasized their
native request indicators over time, and it is doubly unfortunate that
request indicators are not part of the JavaScript ajax APIs.</p>
<p>Be sure not to neglect this significant aspect of your application.
Requests might seem instant when you are working on your application
locally, but in the real world they can take quite a bit longer due to
network latency. It’s often a good idea to take advantage of browser
developer tools that allow you to throttle your local browser’s response
times. This will give you a better idea of what real world users are
seeing, and show you where indicators might help users understand
exactly what is going on.</p>
</div>
</div>
</div>
<p>With this request indicator, we now have a pretty sophisticated user
experience when compared with plain HTML, but we’ve built it all as a
hypermedia-driven feature. No JSON or JavaScript to be seen. And our
implementation has the benefit of being a progressive enhancement; the
application will continue to work for clients that don’t have JavaScript
enabled.</p>
<h2 id="_lazy_loading">Lazy Loading</h2>
<p>With Active Search behind us, let’s move on to a very different sort
of enhancement: lazy loading. Lazy loading is when the loading of a
particular bit of content is deferred until later, when needed. This is
commonly used as a performance enhancement: you avoid the processing
resources necessary to produce some data until that data is actually
needed.</p>
<p>Let’s add a count of the total number of contacts to Contact.app,
just below the bottom of our contacts table. This will give us a
potentially expensive operation that we can use to demonstrate how to
add lazy loading with htmx.</p>
<p>First let’s update our server code in the <code>/contacts</code>
request handler to get a count of the total number of contacts. We will
pass that count through to the template to render some new HTML.</p>
<figure>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a aria-hidden="true" href="#cb15-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb15-2"><a aria-hidden="true" href="#cb15-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb15-3"><a aria-hidden="true" href="#cb15-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>)</span>
<span id="cb15-4"><a aria-hidden="true" href="#cb15-4" tabindex="-1"></a> page <span class="op">=</span> <span class="bu">int</span>(request.args.get(<span class="st">"page"</span>, <span class="dv">1</span>))</span>
<span id="cb15-5"><a aria-hidden="true" href="#cb15-5" tabindex="-1"></a> count <span class="op">=</span> Contact.count() <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb15-6"><a aria-hidden="true" href="#cb15-6" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb15-7"><a aria-hidden="true" href="#cb15-7" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search)</span>
<span id="cb15-8"><a aria-hidden="true" href="#cb15-8" tabindex="-1"></a> <span class="cf">if</span> request.headers.get(<span class="st">'HX-Trigger'</span>) <span class="op">==</span> <span class="st">'search'</span>:</span>
<span id="cb15-9"><a aria-hidden="true" href="#cb15-9" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"rows.html"</span>,</span>
<span id="cb15-10"><a aria-hidden="true" href="#cb15-10" tabindex="-1"></a> contacts<span class="op">=</span>contacts_set, page<span class="op">=</span>page, count<span class="op">=</span>count) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb15-11"><a aria-hidden="true" href="#cb15-11" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb15-12"><a aria-hidden="true" href="#cb15-12" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>(page)</span>
<span id="cb15-13"><a aria-hidden="true" href="#cb15-13" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>,</span>
<span id="cb15-14"><a aria-hidden="true" href="#cb15-14" tabindex="-1"></a> contacts<span class="op">=</span>contacts_set, page<span class="op">=</span>page, count<span class="op">=</span>count)</span></code></pre></div>
<figcaption><p>Adding a count to the UI</p></figcaption>
</figure>
<ol>
<li><p>Get the total count of contacts from the Contact model.</p></li>
<li><p>Pass the count out to the <code>index.html</code> template to use
when rendering.</p></li>
</ol>
<p>As with the rest of the application, in the interest of staying
focused on the <em class="test">hypermedia</em> part of Contact.app, we’ll skip over
the details of how <code>Contact.count()</code> works. We just need to
know that:</p>
<ul>
<li><p>It returns the total count of contacts in the contact
database.</p></li>
<li><p>It may be slow (for the sake of our example).</p></li>
</ul>
<p>Next lets add some HTML to our <code>index.html</code> that takes
advantage of this new bit of data, showing a message next to the “Add
Contact” link with the total count of users. Here is what our HTML looks
like:</p>
<figure>
<div class="sourceCode" id="cb16"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb16-1"><a aria-hidden="true" href="#cb16-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb16-2"><a aria-hidden="true" href="#cb16-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/new"</span><span class="dt">&gt;</span>Add Contact<span class="dt">&lt;/</span><span class="kw">a</span></span>
<span id="cb16-3"><a aria-hidden="true" href="#cb16-3" tabindex="-1"></a> <span class="dt">&gt;</span> <span class="dt">&lt;</span><span class="kw">span</span><span class="dt">&gt;</span>({{ count }} total Contacts)<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb16-4"><a aria-hidden="true" href="#cb16-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding a contact count element to the
application</p></figcaption>
</figure>
<ol>
<li><p>A simple span with some text showing the total number of
contacts.</p></li>
</ol>
<p>Well that was easy, wasn’t it? Now our users will see the total
number of contacts next to the link to add new contacts, to give them a
sense of how large the contact database is. This sort of rapid
development is one of the joys of developing web applications the old
way.</p>
<p><a class="ref" href="#fig-totalcontacts">[fig-totalcontacts]</a> is
what the feature looks like in our application. Beautiful.</p>
<figure id="fig-totalcontacts">
<p><img src="https://hypermedia.systems/images/screenshot_total_contacts.png"/></p>
<figcaption><p>Total contact count display</p></figcaption>
</figure>
<p>Of course, as you probably suspected, all is not perfect.
Unfortunately, upon shipping this feature to production, we start
getting complaints from users that the application “feels slow.” Like
all good developers faced with a performance issue, rather than guessing
what the issue might be, we try to get a performance profile of the
application to see what exactly is causing the problem.</p>
<p>It turns out, surprisingly, that the problem is that innocent looking
<code>Contacts.count()</code> call, which is taking up to a second and a
half to complete. Unfortunately, for reasons beyond the scope of this
book, it is not possible to improve that load time, nor is possible to
cache the result.</p>
<p>This leaves us with two options:</p>
<ul>
<li><p>Remove the feature.</p></li>
<li><p>Come up with some other way to mitigate the performance
issue.</p></li>
</ul>
<p>Let’s assume that we can’t remove the feature, and therefore look at
how we can mitigate this performance issue by using htmx instead.</p>
<h3 id="_pulling_out_the_expensive_code">Pulling Out The Expensive
Code</h3>
<p>The first step in implementing the Lazy Load pattern is to pull the
expensive code — that is, the call to <code>Contacts.count()</code> —
out of the request handler for the <code>/contacts</code> endpoint.</p>
<p>Let’s put this function call into its own HTTP request handler as a
new HTTP endpoint that we will put at <code>/contacts/count</code>. For
this new endpoint, we won’t need to render a template at all: its sole
job is going to be to render that small bit of text that is in the span,
“(22 total Contacts).”</p>
<p>Here is what the new code will look like:</p>
<figure>
<div class="sourceCode" id="cb17"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb17-1"><a aria-hidden="true" href="#cb17-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb17-2"><a aria-hidden="true" href="#cb17-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb17-3"><a aria-hidden="true" href="#cb17-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>)</span>
<span id="cb17-4"><a aria-hidden="true" href="#cb17-4" tabindex="-1"></a> page <span class="op">=</span> <span class="bu">int</span>(request.args.get(<span class="st">"page"</span>, <span class="dv">1</span>)) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb17-5"><a aria-hidden="true" href="#cb17-5" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb17-6"><a aria-hidden="true" href="#cb17-6" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search)</span>
<span id="cb17-7"><a aria-hidden="true" href="#cb17-7" tabindex="-1"></a> <span class="cf">if</span> request.headers.get(<span class="st">'HX-Trigger'</span>) <span class="op">==</span> <span class="st">'search'</span>:</span>
<span id="cb17-8"><a aria-hidden="true" href="#cb17-8" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"rows.html"</span>,</span>
<span id="cb17-9"><a aria-hidden="true" href="#cb17-9" tabindex="-1"></a> contacts<span class="op">=</span>contacts_set, page<span class="op">=</span>page)</span>
<span id="cb17-10"><a aria-hidden="true" href="#cb17-10" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb17-11"><a aria-hidden="true" href="#cb17-11" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>(page)</span>
<span id="cb17-12"><a aria-hidden="true" href="#cb17-12" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>,</span>
<span id="cb17-13"><a aria-hidden="true" href="#cb17-13" tabindex="-1"></a> contacts<span class="op">=</span>contacts_set, page<span class="op">=</span>page) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb17-14"><a aria-hidden="true" href="#cb17-14" tabindex="-1"></a></span>
<span id="cb17-15"><a aria-hidden="true" href="#cb17-15" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/count"</span>)</span>
<span id="cb17-16"><a aria-hidden="true" href="#cb17-16" tabindex="-1"></a><span class="kw">def</span> contacts_count():</span>
<span id="cb17-17"><a aria-hidden="true" href="#cb17-17" tabindex="-1"></a> count <span class="op">=</span> Contact.count() <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb17-18"><a aria-hidden="true" href="#cb17-18" tabindex="-1"></a> <span class="cf">return</span> <span class="st">"("</span> <span class="op">+</span> <span class="bu">str</span>(count) <span class="op">+</span> <span class="st">" total Contacts)"</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Pulling the expensive code out</p></figcaption>
</figure>
<ol>
<li><p>We no longer call <code>Contacts.count()</code> in this
handler.</p></li>
<li><p><code>Count</code> is no longer passed out to the template to
render in the <code>/contacts</code> handler.</p></li>
<li><p>We create a new handler at the <code>/contacts/count</code> path
that does the expensive calculation.</p></li>
<li><p>Return the string with the total number of contacts.</p></li>
</ol>
<p>So now we have moved the performance issue out of the
<code>/contacts</code> handler code, which renders the main contacts
table, and created a new HTTP endpoint that will produce this
expensive-to-create count string for us.</p>
<p>Now we need to get the content from this new handler <em class="test">into</em>
the span, somehow. As we said earlier, the default behavior of htmx is
to place any content it receives for a given request into the
<code>innerHTML</code> of an element, and that turns out to be exactly
what we want here: we want to retrieve this text and put it into the
<code>span</code>. So we can simply place an <code>hx-get</code>
attribute on the span, pointing to this new path, and do exactly
that.</p>
<p>However, recall that the default <em class="test">event</em> that will trigger a
request for a <code>span</code> element in htmx is the
<code>click</code> event. Well, that’s not what we want! Instead, we
want this request to trigger immediately, when the page loads.</p>
<p>To do this, we can add the <code>hx-trigger</code> attribute to
update the trigger of the requests for the element, and use the
<code>load</code> event.</p>
<p>The <code>load</code> event is a special event that htmx triggers on
all content when it is loaded into the DOM. By setting
<code>hx-trigger</code> to <code>load</code>, we will cause htmx to
issue the <code>GET</code> request when the <code>span</code> element is
loaded into the page.</p>
<p>Here is our updated template code:</p>
<figure>
<div class="sourceCode" id="cb18"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb18-1"><a aria-hidden="true" href="#cb18-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb18-2"><a aria-hidden="true" href="#cb18-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/new"</span><span class="dt">&gt;</span>Add Contact<span class="dt">&lt;/</span><span class="kw">a</span></span>
<span id="cb18-3"><a aria-hidden="true" href="#cb18-3" tabindex="-1"></a> <span class="dt">&gt;</span> <span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/count"</span><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"load"</span><span class="dt">&gt;&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb18-4"><a aria-hidden="true" href="#cb18-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding a contact count element to the
application</p></figcaption>
</figure>
<ol>
<li><p>Issue a <code>GET</code> to <code>/contacts/count</code> when the
<code>load</code> event occurs.</p></li>
</ol>
<p>Note that the <code>span</code> starts empty: we have removed the
content from it, and we are allowing the request to
<code>/contacts/count</code> to populate it instead.</p>
<p>And, check it out, our <code>/contacts</code> page is fast again!
When you navigate to the page it feels very snappy and profiling shows
that yes, indeed, the page is loading much more quickly. Why is that?
Well, we’ve deferred the expensive calculation to a secondary request,
allowing the initial request to finish loading faster.</p>
<p>You might say “OK, great, but it’s still taking a second or two to
get the total count on the page.” True, but often the user may not be
particularly interested in the total count. They may just want to come
to the page and search for an existing user, or perhaps they may want to
edit or add a user. The total count of contacts is just a “nice to have”
bit of information in these cases.</p>
<p>By deferring the calculation of the count in this manner we let users
get on with their use of the application while we perform the expensive
calculation.</p>
<p>Yes, the total time to get all the information on the screen takes
just as long. It actually will be a bit longer, since we now need two
HTTP requests to get all the information for the page. But the
<em class="test">perceived performance</em> for the end user will be much better:
they can do what they want nearly immediately, even if some information
isn’t available instantaneously.</p>
<p>Lazy Loading is a great tool to have in your belt when optimizing web
application performance.</p>
<h3 id="_adding_an_indicator">Adding An Indicator</h3>
<p>A shortcoming of the current implementation is that currently there
is no indication that the count request is in flight, it just appears at
some point when the request finishes.</p>
<p>This isn’t ideal. What we want here is an indicator, just like we
added in our Active Search example. And, in fact, we can simply reuse
that same exact spinner image, copy-and-pasted into the new HTML we have
created.</p>
<p>Now, in this case, we have a one-time request and, once the request
is over, we are not going to need the spinner anymore. So it doesn’t
make sense to use the exact same approach we did with the active search
example. Recall that in that case we placed a spinner <em class="test">after</em> the
span and using the <code>hx-indicator</code> attribute to point to
it.</p>
<p>In this case, since the spinner is only used once, we can put it
<em class="test">inside</em> the content of the span. When the request completes the
content in the response will be placed inside the span, replacing the
spinner with the computed contact count. It turns out that htmx allows
you to place indicators with the <code>htmx-indicator</code> class on
them inside of elements that issue htmx-powered requests. In the absence
of an <code>hx-indicator</code> attribute, these internal indicators
will be shown when a request is in flight.</p>
<p>So let’s add that spinner from the active search example as the
initial content in our span:</p>
<figure>
<div class="sourceCode" id="cb19"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb19-1"><a aria-hidden="true" href="#cb19-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/count"</span><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"load"</span><span class="dt">&gt;</span></span>
<span id="cb19-2"><a aria-hidden="true" href="#cb19-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">img</span><span class="ot"> id</span><span class="op">=</span><span class="st">"spinner"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"htmx-indicator"</span></span>
<span id="cb19-3"><a aria-hidden="true" href="#cb19-3" tabindex="-1"></a><span class="ot"> src</span><span class="op">=</span><span class="st">"/static/img/spinning-circles.svg"</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb19-4"><a aria-hidden="true" href="#cb19-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding an indicator to our lazily loaded
content</p></figcaption>
</figure>
<ol>
<li><p>Yep, that’s it.</p></li>
</ol>
<p>Now when the user loads the page, rather than having the total
contact count magically appear, there is a nice spinner indicating that
something is coming. Much better.</p>
<p>Note that all we had to do was copy and paste our indicator from the
active search example into the <code>span</code>. Once again we see how
htmx provides flexible, composable features and building blocks.
Implementing a new feature is often just copy-and-paste, maybe a tweak
or two, and you are done.</p>
<h3 id="_but_thats_not_lazy">But That’s Not Lazy!</h3>
<p>You might say “OK, but that’s not really lazy. We are still loading
the count immediately when the page is loaded, we are just doing it in a
second request. You aren’t really waiting until the value is actually
needed.”</p>
<p>Fine. Let’s make it <em class="test">lazy</em> lazy: we’ll only issue the request
when the <code>span</code> scrolls into view.</p>
<p>To do that, let’s recall how we set up the infinite scroll example:
we used the <code>revealed</code> event for our trigger. That’s all we
want here, right? When the element is revealed we issue the request?</p>
<p>Yep, that’s it. Once again, we can mix and match concepts across
various UX patterns to come up with solutions to new problems in
htmx.</p>
<figure>
<div class="sourceCode" id="cb20"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb20-1"><a aria-hidden="true" href="#cb20-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">span</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/count"</span><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"revealed"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb20-2"><a aria-hidden="true" href="#cb20-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">img</span><span class="ot"> id</span><span class="op">=</span><span class="st">"spinner"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"htmx-indicator"</span></span>
<span id="cb20-3"><a aria-hidden="true" href="#cb20-3" tabindex="-1"></a><span class="ot"> src</span><span class="op">=</span><span class="st">"/static/img/spinning-circles.svg"</span><span class="dt">/&gt;</span></span>
<span id="cb20-4"><a aria-hidden="true" href="#cb20-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Making it truly lazy</p></figcaption>
</figure>
<ol>
<li><p>Change the <code>hx-trigger</code> to
<code>revealed</code>.</p></li>
</ol>
<p>Now we have a truly lazy implementation, deferring the expensive
computation until we are absolutely sure we need it. A pretty cool
trick, and, again, a simple one-attribute change demonstrates the
flexibility of both htmx and the hypermedia approach.</p>
<h2 id="_inline_delete">Inline Delete</h2>
<p>For our next hypermedia trick, we are going to implement the “Inline
Delete” pattern. With this feature, a contact can be deleted directly
from the table of all contacts, rather than requiring the user to
navigate all the way to the edit view of particular contact, in order to
access the “Delete Contact” button we added in the last chapter.</p>
<p>Recall that we already have “Edit” and “View” links for each row, in
the <code>rows.html</code> template:</p>
<figure>
<div class="sourceCode" id="cb21"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb21-1"><a aria-hidden="true" href="#cb21-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb21-2"><a aria-hidden="true" href="#cb21-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb21-3"><a aria-hidden="true" href="#cb21-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb21-4"><a aria-hidden="true" href="#cb21-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The existing row actions</p></figcaption>
</figure>
<p>Now we want to add a “Delete” link as well. And, thinking on it, we
want that link to act an awful lot like the “Delete Contact” button from
<code>edit.html</code>, don’t we? We’d like to issue an HTTP
<code>DELETE</code> to the URL for the given contact and we want a
confirmation dialog to ensure the user doesn’t accidentally delete a
contact.</p>
<p>Here is the “Delete Contact” button html:</p>
<figure>
<div class="sourceCode" id="cb22"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb22-1"><a aria-hidden="true" href="#cb22-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span></span>
<span id="cb22-2"><a aria-hidden="true" href="#cb22-2" tabindex="-1"></a><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span></span>
<span id="cb22-3"><a aria-hidden="true" href="#cb22-3" tabindex="-1"></a><span class="ot"> hx-push-url</span><span class="op">=</span><span class="st">"true"</span></span>
<span id="cb22-4"><a aria-hidden="true" href="#cb22-4" tabindex="-1"></a><span class="ot"> hx-confirm</span><span class="op">=</span><span class="st">"Are you sure you want to delete this contact?"</span></span>
<span id="cb22-5"><a aria-hidden="true" href="#cb22-5" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span></span>
<span id="cb22-6"><a aria-hidden="true" href="#cb22-6" tabindex="-1"></a> Delete Contact</span>
<span id="cb22-7"><a aria-hidden="true" href="#cb22-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The existing row actions</p></figcaption>
</figure>
<p>As you may suspect by now, this is going to be another copy-and-paste
job.</p>
<p>One thing to note is that, in the case of the “Delete Contact”
button, we wanted to re-render the whole screen and update the URL,
since we are going to be returning from the edit view for the contact to
the list view of all contacts. In the case of this link, however, we are
already on the list of contacts, so there is no need to update the URL,
and we can omit the <code>hx-push-url</code> attribute.</p>
<p>Here is the code for our inline “Delete” link:</p>
<figure>
<div class="sourceCode" id="cb23"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb23-1"><a aria-hidden="true" href="#cb23-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb23-2"><a aria-hidden="true" href="#cb23-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb23-3"><a aria-hidden="true" href="#cb23-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb23-4"><a aria-hidden="true" href="#cb23-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"#"</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span></span>
<span id="cb23-5"><a aria-hidden="true" href="#cb23-5" tabindex="-1"></a><span class="ot"> hx-confirm</span><span class="op">=</span><span class="st">"Are you sure you want to delete this contact?"</span></span>
<span id="cb23-6"><a aria-hidden="true" href="#cb23-6" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span>Delete<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb23-7"><a aria-hidden="true" href="#cb23-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The existing row actions</p></figcaption>
</figure>
<ol>
<li><p>Almost a straight copy of the “Delete Contact” button.</p></li>
</ol>
<p>As you can see, we have added a new anchor tag and given it a blank
target (the <code>#</code> value in its <code>href</code> attribute) to
retain the correct mouse-over styling behavior of the link. We’ve also
copied the <code>hx-delete</code>, <code>hx-confirm</code> and
<code>hx-target</code> attributes from the “Delete Contact” button, but
omitted the <code>hx-push-url</code> attributes since we don’t want to
update the URL of the browser.</p>
<p>We now have inline delete working, even with a confirmation dialog. A
user can click on the “Delete” link and the row will disappear from the
UI as the entire page is re-rendered.</p>
<div id="sidebar">
<div>
<div>
<p><strong>A Style Sidebar</strong></p>
</div>
<div>
<p>One side effect of adding this delete link is that we are starting to
pile up the actions in a contact row:</p>
<figure id="fig-stacked-actions">
<p><img src="https://hypermedia.systems/images/screenshot_stacked_actions.png"/></p>
<figcaption><p>That’s a lot of actions</p></figcaption>
</figure>
<p>It would be nice if we didn’t show the actions all in a row, and,
additionally, it would be nice if we only showed the actions when the
user indicated interest in a given row. We will return to this problem
after we look at the relationship between scripting and a
Hypermedia-Driven Application in a later chapter.</p>
<p>For now, let’s just tolerate this less-than-ideal user interface,
knowing that we will fix it later.</p>
</div>
</div>
</div>
<h3 id="_narrowing_our_target">Narrowing Our Target</h3>
<p>We can get even fancier here, however. What if, rather than
re-rendering the whole page, we just removed the row for the contact?
The user is looking at the row anyway, so is there really a need to
re-render the whole page?</p>
<p>To do this, we’ll need to do a couple of things:</p>
<ul>
<li><p>We’ll need to update this link to target the row that it is
in.</p></li>
<li><p>We’ll need to change the swap to <code>outerHTML</code>, since we
want to replace (really, remove) the entire row.</p></li>
<li><p>We’ll need to update the server side to render empty content when
the <code>DELETE</code> is issued from a “Delete” link rather than from
the “Delete Contact” button on the contact edit page.</p></li>
</ul>
<p>First things first, update the target of our “Delete” link to be the
row that the link is in, rather than the entire body. We can once again
take advantage of the relative positional <code>closest</code> feature
to target the closest <code>tr</code>, like we did in our “Click To
Load” and “Infinite Scroll” features:</p>
<figure>
<div class="sourceCode" id="cb24"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb24-1"><a aria-hidden="true" href="#cb24-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb24-2"><a aria-hidden="true" href="#cb24-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb24-3"><a aria-hidden="true" href="#cb24-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb24-4"><a aria-hidden="true" href="#cb24-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"#"</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span></span>
<span id="cb24-5"><a aria-hidden="true" href="#cb24-5" tabindex="-1"></a><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span></span>
<span id="cb24-6"><a aria-hidden="true" href="#cb24-6" tabindex="-1"></a><span class="ot"> hx-confirm</span><span class="op">=</span><span class="st">"Are you sure you want to delete this contact?"</span></span>
<span id="cb24-7"><a aria-hidden="true" href="#cb24-7" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"closest tr"</span><span class="dt">&gt;</span>Delete<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb24-8"><a aria-hidden="true" href="#cb24-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The existing row actions</p></figcaption>
</figure>
<ol>
<li><p>Updated to target the closest enclosing <code>tr</code> (table
row) of the link.</p></li>
</ol>
<h3 id="_updating_the_server_side">Updating The Server Side</h3>
<p>Now we need to update the server side. We want to keep the “Delete
Contact” button working as well, and in that case the current logic is
correct. So we’ll need some way to differentiate between
<code>DELETE</code> requests that are triggered by the button and
<code>DELETE</code> requests that come from this anchor.</p>
<p>The cleanest way to do this is to add an <code>id</code> attribute to
the “Delete Contact” button, so that we can inspect the
<code>HX-Trigger</code> HTTP Request header to determine if the delete
button was the cause of the request. This is a simple change to the
existing HTML:</p>
<figure>
<div class="sourceCode" id="cb25"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb25-1"><a aria-hidden="true" href="#cb25-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> id</span><span class="op">=</span><span class="st">"delete-btn"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb25-2"><a aria-hidden="true" href="#cb25-2" tabindex="-1"></a> hx-delete="/contacts/{{ contact.id }}"</span>
<span id="cb25-3"><a aria-hidden="true" href="#cb25-3" tabindex="-1"></a> hx-push-url="true"</span>
<span id="cb25-4"><a aria-hidden="true" href="#cb25-4" tabindex="-1"></a> hx-confirm="Are you sure you want to delete this contact?"</span>
<span id="cb25-5"><a aria-hidden="true" href="#cb25-5" tabindex="-1"></a> hx-target="body"&gt;</span>
<span id="cb25-6"><a aria-hidden="true" href="#cb25-6" tabindex="-1"></a> Delete Contact</span>
<span id="cb25-7"><a aria-hidden="true" href="#cb25-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding an <code>id</code> to the “delete contact”
button</p></figcaption>
</figure>
<ol>
<li><p>An <code>id</code> attribute has been added to the
button.</p></li>
</ol>
<p>By giving this button an id attribute, we now have a mechanism for
differentiating between the delete button in the <code>edit.html</code>
template and the delete links in the <code>rows.html</code> template.
When this button issues a request, it will look something like this:</p>
<figure>
<pre class="http"><code>DELETE http://example.org/contacts/42 HTTP/1.1
Accept: text/html,*/*
Host: example.org
...
HX-Trigger: delete-btn
...
</code></pre>
</figure>
<p>You can see that the request now includes the <code>id</code> of the
button. This allows us to write code very similar to what we did for the
active search pattern, using a conditional on the
<code>HX-Trigger</code> header to determine what we want to do. If that
header has the value <code>delete-btn</code>, then we know the request
came from the button on the edit page, and we can do what we are
currently doing: delete the contact and redirect to
<code>/contacts</code> page.</p>
<p>If it <em class="test">does not</em> have that value, then we can simply delete
the contact and return an empty string. This empty string will replace
the target, in this case the row for the given contact, thereby removing
the row from the UI.</p>
<p>Let’s refactor our server-side code to do this:</p>
<figure>
<div class="sourceCode" id="cb27"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb27-1"><a aria-hidden="true" href="#cb27-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/&lt;contact_id&gt;"</span>, methods<span class="op">=</span>[<span class="st">"DELETE"</span>])</span>
<span id="cb27-2"><a aria-hidden="true" href="#cb27-2" tabindex="-1"></a><span class="kw">def</span> contacts_delete(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb27-3"><a aria-hidden="true" href="#cb27-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb27-4"><a aria-hidden="true" href="#cb27-4" tabindex="-1"></a> contact.delete()</span>
<span id="cb27-5"><a aria-hidden="true" href="#cb27-5" tabindex="-1"></a> <span class="cf">if</span> request.headers.get(<span class="st">'HX-Trigger'</span>) <span class="op">==</span> <span class="st">'delete-btn'</span>: <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb27-6"><a aria-hidden="true" href="#cb27-6" tabindex="-1"></a> flash(<span class="st">"Deleted Contact!"</span>)</span>
<span id="cb27-7"><a aria-hidden="true" href="#cb27-7" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">"/contacts"</span>, <span class="dv">303</span>)</span>
<span id="cb27-8"><a aria-hidden="true" href="#cb27-8" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb27-9"><a aria-hidden="true" href="#cb27-9" tabindex="-1"></a> <span class="cf">return</span> <span class="st">""</span> <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Updating our server code to handle two different delete)
patterns</p></figcaption>
</figure>
<ol>
<li><p>If the delete button on the edit page submitted this request,
then continue to do the previous logic.</p></li>
<li><p>If not, simply return an empty string, which will delete the
row.</p></li>
</ol>
<p>And that’s our server-side implementation: when a user clicks
“Delete” on a contact row and confirms the delete, the row will
disappear from the UI. Once again, we have a situation where just
changing a few lines of simple code gives us a dramatically different
behavior. Hypermedia is powerful in this manner.</p>
<h3 id="_the_htmx_swapping_model">The Htmx Swapping Model</h3>
<p>This is pretty cool, but there is another improvement we can make if
we take some time to understand the htmx content swapping model: it
would be nice if, rather than just instantly deleting the row, we faded
it out before we removed it. The fade would make it clear that the row
is being removed, giving the user some nice visual feedback on the
deletion.</p>
<p>It turns out we can do this pretty easily with htmx, but to do so
we’ll need to dig in to exactly how htmx swaps content.</p>
<p>You might think that htmx simply puts the new content into the DOM,
but that’s not in fact how it works. Instead, content goes through a
series of steps as it is added to the DOM:</p>
<ul>
<li><p>When content is received and about to be swapped into the DOM,
the <code>htmx-swapping</code> CSS class is added to the target
element.</p></li>
<li><p>A small delay then occurs (we will discuss why this delay exists
in a moment).</p></li>
<li><p>Next, the <code>htmx-swapping</code> class is removed from the
target and the <code>htmx-settling</code> class is added.</p></li>
<li><p>The new content is swapped into the DOM.</p></li>
<li><p>Another small delay occurs.</p></li>
<li><p>Finally, the <code>htmx-settling</code> class is removed from the
target.</p></li>
</ul>
<p>There is more to the swap mechanic (settling, for example, is a more
advanced topic that we will discuss in a later chapter) but this is
enough for now.</p>
<p>Now, there are small delays in the process here, typically on the
order of a few milliseconds. Why so? It turns out that these small
delays allow <em class="test">CSS transitions</em> to occur.</p>
<div id="sidebar">
<div>
<div>
<p><strong>CSS Transitions</strong></p>
</div>
<div>
<p>CSS transitions are a technology that allow you to animate a
transition from one style to another. So, for example, if you changed
the height of something from 10 pixels to 20 pixels, by using a CSS
transition you can make the element smoothly animate to the new height.
These sorts of animations are fun, often increase application usability,
and are a great mechanism to add polish to your web application.</p>
</div>
</div>
</div>
<p>Unfortunately, CSS transitions are difficult to access in plain HTML:
you usually have to use JavaScript and add or remove classes to get them
to trigger. This is why the htmx swap model is more complicated than you
might initially think. By swapping in classes and adding small delays,
you can access CSS transitions purely within HTML, without needing to
write any JavaScript!</p>
<h3 id="_taking_advantage_of_htmx_swapping">Taking Advantage of
“htmx-swapping”</h3>
<p>OK, so, let’s go back and look at our inline delete mechanic: we
click an htmx-enhanced link which deletes the contact and then swaps
some empty content in for the row. We know that before the
<code>tr</code> element is removed, it will have the
<code>htmx-swapping</code> class added to it. We can take advantage of
that to write a CSS transition that fades the opacity of the row to 0.
Here is what that CSS looks like:</p>
<figure>
<div class="sourceCode" id="cb28"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb28-1"><a aria-hidden="true" href="#cb28-1" tabindex="-1"></a>tr<span class="fu">.htmx-swapping</span> { &lt;1<span class="op">&gt;</span></span>
<span id="cb28-2"><a aria-hidden="true" href="#cb28-2" tabindex="-1"></a> opacity<span class="in">:</span> 0; &lt;2<span class="op">&gt;</span></span>
<span id="cb28-3"><a aria-hidden="true" href="#cb28-3" tabindex="-1"></a> transition<span class="in">:</span> opacity 1s ease-out; &lt;3<span class="op">&gt;</span></span>
<span id="cb28-4"><a aria-hidden="true" href="#cb28-4" tabindex="-1"></a>}</span></code></pre></div>
<figcaption><p>Adding a fade out transition</p></figcaption>
</figure>
<ol>
<li><p>We want this style to apply to <code>tr</code> elements with the
<code>htmx-swapping</code> class on them.</p></li>
<li><p>The <code>opacity</code> will be 0, making it invisible.</p></li>
<li><p>The <code>opacity</code> will transition to 0 over a 1 second
time period, using the <code>ease-out</code> function.</p></li>
</ol>
<p>Again, this is not a CSS book and we are not going to go deeply into
the details of CSS transitions, but hopefully the above makes sense to
you, even if this is the first time you’ve seen CSS transitions.</p>
<p>So, think about what this means from the htmx swapping model: when
htmx gets content back to swap into the row it will put the
<code>htmx-swapping</code> class on the row and wait a bit. This will
allow the transition to a zero opacity to occur, fading the row out.
Then the new (empty) content will be swapped in, which will effectively
remove the row.</p>
<p>Sounds good, and we are nearly there. There is one more thing we need
to do: the default “swap delay” for htmx is very short, a few
milliseconds. That makes sense in most cases: you don’t want to have
much of a delay before you put the new content into the DOM. But, in
this case, we want to give the CSS animation time to complete before we
do the swap, we want to give it a second, in fact.</p>
<p>Fortunately htmx has an option for the <code>hx-swap</code>
annotation that allows you to set the swap delay: following the swap
type you can add <code>swap:</code> followed by a timing value to tell
htmx to wait a specific amount of time before it swaps. Let’s update our
HTML to allow a one second delay before the swap is done for the delete
action:</p>
<figure>
<div class="sourceCode" id="cb29"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb29-1"><a aria-hidden="true" href="#cb29-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb29-2"><a aria-hidden="true" href="#cb29-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb29-3"><a aria-hidden="true" href="#cb29-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb29-4"><a aria-hidden="true" href="#cb29-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">"#"</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span></span>
<span id="cb29-5"><a aria-hidden="true" href="#cb29-5" tabindex="-1"></a><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML swap:1s"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb29-6"><a aria-hidden="true" href="#cb29-6" tabindex="-1"></a> hx-confirm="Are you sure you want to delete this contact?"</span>
<span id="cb29-7"><a aria-hidden="true" href="#cb29-7" tabindex="-1"></a> hx-target="closest tr"&gt;Delete<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb29-8"><a aria-hidden="true" href="#cb29-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The existing row actions</p></figcaption>
</figure>
<ol>
<li><p>A swap delay changes how long htmx waits before it swaps in new
content.</p></li>
</ol>
<p>With this modification, the existing row will stay in the DOM for an
additional second, with the <code>htmx-swapping</code> class on it. This
will give the row time to transition to an opacity of zero, giving the
fade out effect we want.</p>
<p>Now, when a user clicks on a “Delete” link and confirms the delete,
the row will slowly fade out and then, once it has faded to a 0 opacity,
it will be removed. Pretty fancy, and all done in a declarative,
hypermedia-oriented manner, no JavaScript required. (Well, obviously
htmx is written in JavaScript, but you know what we mean: we didn’t have
to write any JavaScript to implement the feature.)</p>
<h2 id="_bulk_delete">Bulk Delete</h2>
<p>The final feature we are going to implement in this chapter is a
“Bulk Delete.” The current mechanism for deleting users is nice, but it
would be annoying if a user wanted to delete five or ten contacts at a
time, wouldn’t it? For the bulk delete feature, we want to add the
ability to select rows via a checkbox input and delete them all in a
single go by clicking a “Delete Selected Contacts” button.</p>
<p>To get started with this feature, we’ll need to add a checkbox input
to each row in the <code>rows.html</code> template. This input will have
the name <code>selected_contact_ids</code> and its value will be the
<code>id</code> of the contact for the current row.</p>
<p>Here is what the updated code for <code>rows.html</code> looks
like:</p>
<figure>
<div class="sourceCode" id="cb30"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb30-1"><a aria-hidden="true" href="#cb30-1" tabindex="-1"></a>{% for contact in contacts %}</span>
<span id="cb30-2"><a aria-hidden="true" href="#cb30-2" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb30-3"><a aria-hidden="true" href="#cb30-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"checkbox"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"selected_contact_ids"</span></span>
<span id="cb30-4"><a aria-hidden="true" href="#cb30-4" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.id }}"</span><span class="dt">&gt;&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb30-5"><a aria-hidden="true" href="#cb30-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>{{ contact.first }}<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb30-6"><a aria-hidden="true" href="#cb30-6" tabindex="-1"></a> ... omitted</span>
<span id="cb30-7"><a aria-hidden="true" href="#cb30-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span id="cb30-8"><a aria-hidden="true" href="#cb30-8" tabindex="-1"></a>{% endfor %}</span></code></pre></div>
<figcaption><p>Adding a checkbox to each row</p></figcaption>
</figure>
<ol>
<li><p>A new cell with the checkbox input whose value is set to the
current contact’s id.</p></li>
</ol>
<p>We’ll also need to add an empty column in the header for the table to
accommodate the checkbox column. With that done we now get a series of
check boxes, one for each row, a pattern no doubt familiar to you from
the web (<a class="ref" href="#fig-checkboxes">[fig-checkboxes]</a>).</p>
<figure id="fig-checkboxes">
<p><img src="https://hypermedia.systems/images/screenshot_checkboxes.png"/></p>
<figcaption><p>Checkboxes for our contact rows</p></figcaption>
</figure>
<p>If you are not familiar with or have forgotten the way checkboxes
work in HTML: a checkbox will submit its value associated with the name
of the input if and only if it is checked. So if, for example, you
checked the contacts with the ids 3, 7 and 9, then those three values
would all be submitted to the server. Since all the checkboxes in this
case have the same name, <code>selected_contact_ids</code>, all three
values would be submitted with the name
<code>selected_contact_ids</code>.</p>
<h3 id="_the_delete_selected_contacts_button">The “Delete Selected
Contacts” Button</h3>
<p>The next step is to add a button below the table that will delete all
the selected contacts. We want this button, like our delete links in
each row, to issue an HTTP <code>DELETE</code>, but rather than issuing
it to the URL for a given contact, like we do with the inline delete
links and with the delete button on the edit page, here we want to issue
the <code>DELETE</code> to the <code>/contacts</code> URL.</p>
<p>As with the other delete elements, we want to confirm that the user
wishes to delete the contacts, and, for this case, we are going to
target the body of page, since we are going to re-render the whole
table.</p>
<p>Here is what the button code looks like:</p>
<figure>
<div class="sourceCode" id="cb31"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb31-1"><a aria-hidden="true" href="#cb31-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span></span>
<span id="cb31-2"><a aria-hidden="true" href="#cb31-2" tabindex="-1"></a><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb31-3"><a aria-hidden="true" href="#cb31-3" tabindex="-1"></a> hx-confirm="Are you sure you want to delete these contacts?" <span class="er">&lt;</span>2&gt;</span>
<span id="cb31-4"><a aria-hidden="true" href="#cb31-4" tabindex="-1"></a> hx-target="body"&gt; <span class="er">&lt;</span>3&gt;</span>
<span id="cb31-5"><a aria-hidden="true" href="#cb31-5" tabindex="-1"></a> Delete Selected Contacts</span>
<span id="cb31-6"><a aria-hidden="true" href="#cb31-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The “delete selected contacts” button</p></figcaption>
</figure>
<ol>
<li><p>Issue a <code>DELETE</code> to <code>/contacts</code>.</p></li>
<li><p>Confirm that the user wants to delete the selected
contacts.</p></li>
<li><p>Target the body.</p></li>
</ol>
<p>Pretty easy. One question though: how are we going to include the
values of all the selected checkboxes in the request? As it stands right
now, this is just a stand-alone button, and it doesn’t have any
information indicating that it should include any other information in
the <code>DELETE</code> request it makes.</p>
<p>Fortunately, htmx has a few different ways to include values of
inputs with a request.</p>
<p>One way would be to use the <code>hx-include</code> attribute, which
allows you to use a CSS selector to specify the elements you want to
include in the request. That would work fine here, but we are going to
use another approach that is a bit simpler in this case.</p>
<p>By default, if an element is a child of a <code>form</code> element
and makes a non-<code>GET</code> request, htmx will include all the
values of inputs within that form. In situations like this, where there
is a bulk operation for a table, it is common to enclose the whole table
in a form tag, so that it is easy to add buttons that operate on the
selected items.</p>
<p>Let’s add that form tag around the table, and be sure to enclose the
button in it as well:</p>
<figure>
<div class="sourceCode" id="cb32"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb32-1"><a aria-hidden="true" href="#cb32-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb32-2"><a aria-hidden="true" href="#cb32-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">table</span><span class="dt">&gt;</span></span>
<span id="cb32-3"><a aria-hidden="true" href="#cb32-3" tabindex="-1"></a> ... omitted</span>
<span id="cb32-4"><a aria-hidden="true" href="#cb32-4" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span>
<span id="cb32-5"><a aria-hidden="true" href="#cb32-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span></span>
<span id="cb32-6"><a aria-hidden="true" href="#cb32-6" tabindex="-1"></a><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb32-7"><a aria-hidden="true" href="#cb32-7" tabindex="-1"></a><span class="ot"> hx-confirm</span><span class="op">=</span><span class="st">"Are you sure you want to delete these contacts?"</span></span>
<span id="cb32-8"><a aria-hidden="true" href="#cb32-8" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span></span>
<span id="cb32-9"><a aria-hidden="true" href="#cb32-9" tabindex="-1"></a> Delete Selected Contacts</span>
<span id="cb32-10"><a aria-hidden="true" href="#cb32-10" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb32-11"><a aria-hidden="true" href="#cb32-11" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span></code></pre></div>
<figcaption><p>The “delete selected contacts” button</p></figcaption>
</figure>
<ol>
<li><p>The form tag encloses the entire table.</p></li>
<li><p>The form tag also encloses the button.</p></li>
</ol>
<p>Now, when the button issues a <code>DELETE</code>, it will include
all the contact ids that have been selected as the
<code>selected_contact_ids</code> request variable.</p>
<h3 id="_the_server_side_for_delete_selected_contacts">The Server Side
for Delete Selected Contacts</h3>
<p>The server-side implementation is going to look like our original
server-side code for deleting a contact. In fact, once again, we can
just copy and paste, and make a few fixes:</p>
<ul>
<li><p>We want to change the URL to <code>/contacts</code>.</p></li>
<li><p>We want the handler to get <em class="test">all</em> the ids submitted as
<code>selected_contact_ids</code> and iterate over each one, deleting
the given contact.</p></li>
</ul>
<p>Those are the only changes we need to make! Here is what the
server-side code looks like:</p>
<figure>
<div class="sourceCode" id="cb33"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb33-1"><a aria-hidden="true" href="#cb33-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/"</span>, methods<span class="op">=</span>[<span class="st">"DELETE"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb33-2"><a aria-hidden="true" href="#cb33-2" tabindex="-1"></a><span class="kw">def</span> contacts_delete_all():</span>
<span id="cb33-3"><a aria-hidden="true" href="#cb33-3" tabindex="-1"></a> contact_ids <span class="op">=</span> [</span>
<span id="cb33-4"><a aria-hidden="true" href="#cb33-4" tabindex="-1"></a> <span class="bu">int</span>(<span class="bu">id</span>)</span>
<span id="cb33-5"><a aria-hidden="true" href="#cb33-5" tabindex="-1"></a> <span class="co"># note: in htmx 1.0 we would use the request.form property instead</span></span>
<span id="cb33-6"><a aria-hidden="true" href="#cb33-6" tabindex="-1"></a> <span class="cf">for</span> <span class="bu">id</span> <span class="kw">in</span> request.args.getlist(<span class="st">"selected_contact_ids"</span>)</span>
<span id="cb33-7"><a aria-hidden="true" href="#cb33-7" tabindex="-1"></a> ] <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb33-8"><a aria-hidden="true" href="#cb33-8" tabindex="-1"></a> <span class="cf">for</span> contact_id <span class="kw">in</span> contact_ids: <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb33-9"><a aria-hidden="true" href="#cb33-9" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb33-10"><a aria-hidden="true" href="#cb33-10" tabindex="-1"></a> contact.delete() <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span>
<span id="cb33-11"><a aria-hidden="true" href="#cb33-11" tabindex="-1"></a> flash(<span class="st">"Deleted Contacts!"</span>) <span class="op">&lt;</span><span class="dv">5</span><span class="op">&gt;</span></span>
<span id="cb33-12"><a aria-hidden="true" href="#cb33-12" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>()</span>
<span id="cb33-13"><a aria-hidden="true" href="#cb33-13" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>, contacts<span class="op">=</span>contacts_set)</span></code></pre></div>
<figcaption><p>The “delete selected contacts” button</p></figcaption>
</figure>
<ol>
<li><p>We handle a <code>DELETE</code> request to the
<code>/contacts/</code> path.</p></li>
<li><p>Convert the <code>selected_contact_ids</code> values submitted to
the server from a list of strings to a list integers.</p></li>
<li><p>Iterate over all of the ids.</p></li>
<li><p>Delete the given contact with each id.</p></li>
<li><p>Beyond that, it’s the same code as our original delete handler:
flash a message and render the <code>index.html</code>
template.</p></li>
</ol>
<p>So, we took the original delete logic and slightly modified it to
deal with an array of ids, rather than a single id.</p>
<p>You might notice one other small change: we did away with the
redirect that was in the original delete code. We did so because we are
already on the page we want to re-render, so there is no reason to
redirect and have the URL update to something new. We can just re-render
the page, and the new list of contacts (sans the contacts that were
deleted) will be re-rendered.</p>
<p>And there we go, we now have a bulk delete feature for our
application. Once again, not a huge amount of code, and we are
implementing these features entirely by exchanging hypermedia with a
server in the traditional, RESTful manner of the web.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Accessible by Default?</h2>
<p>Accessibility problems can arise when we try to implement controls
that aren’t built into HTML.</p>
<p>Earlier, in Chapter 1, we looked at the example of a &lt;div&gt;
improvised to work like a button. Let’s look at a different example:
what if you make something that looks like a set of tabs, but you use
radio buttons and CSS hacks to build it? It’s a neat hack that makes the
rounds in web development communities from time to time.</p>
<p>The problem here is that tabs have requirements beyond clicking to
change content. Your improvised tabs may be missing features that will
lead to user confusion and frustration, as well as some undesirable
behaviors. From the <a href="https://www.w3.org/WAI/ARIA/apg/patterns/tabs/">ARIA Authoring
Practices Guide on tabs</a>:</p>
<ul>
<li><p>Keyboard interaction</p>
<ul>
<li><p>Can the tabs be focused with the Tab key?</p></li>
</ul></li>
<li><p>ARIA roles, states, and properties</p>
<ul>
<li><p>“[The element that contains the tabs] has role
<code>tablist</code>.”</p></li>
<li><p>“Each [tab] has role <code>tab</code> […​]”</p></li>
<li><p>“Each element that contains the content panel for a
<code>tab</code> has role <code>tabpanel</code>.”</p></li>
<li><p>“Each [tab] has the property <code>aria-controls</code> referring
to its associated tabpanel element.”</p></li>
<li><p>“The active <code>tab</code> element has the state
<code>aria-selected</code> set to <code>true</code> and all other
<code>tab</code> elements have it set to <code>false</code>.”</p></li>
<li><p>“Each element with role <code>tabpanel</code> has the property
<code>aria-labelledby</code> referring to its associated
<code>tab</code> element.”</p></li>
</ul></li>
</ul>
<p>You would need to write a lot of code to make your improvised tabs
fulfill all of these requirements. Some of the ARIA attributes can be
added directly in HTML, but they are repetitive and others (like
<code>aria-selected</code>) need to be set through JavaScript since they
are dynamic. The keyboard interactions can be error-prone too.</p>
<p>It’s not impossible, not even that hard, to make your own tab set
implementation. However, it’s difficult to trust that a new
implementation will work for all users in all environments, since most
of us have limited resources for testing.</p>
<p><em class="test">Stick with established libraries</em> for UI interactions. If a
use case requires a bespoke solution, <em class="test">test exhaustively</em> for
keyboard interaction and accessibility. Test manually. Test
automatically. Test with screen readers, test with a keyboard, test on
different browsers and hardware, and run linters (while coding and/or in
CI). Testing is critical to ensure machine readability, or human
readability, or page weight.</p>
<p>Also consider: Does the information need to be presented as tabs?
Sometimes the answer is yes, but if not, a sequence of details and
disclosures fulfills a very similar purpose.</p>
<figure>
<div class="sourceCode" id="cb34"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb34-1"><a aria-hidden="true" href="#cb34-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">details</span><span class="dt">&gt;&lt;</span><span class="kw">summary</span><span class="dt">&gt;</span>Disclosure 1<span class="dt">&lt;/</span><span class="kw">summary</span><span class="dt">&gt;</span></span>
<span id="cb34-2"><a aria-hidden="true" href="#cb34-2" tabindex="-1"></a> Disclosure 1 contents</span>
<span id="cb34-3"><a aria-hidden="true" href="#cb34-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">details</span><span class="dt">&gt;</span></span>
<span id="cb34-4"><a aria-hidden="true" href="#cb34-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">details</span><span class="dt">&gt;&lt;</span><span class="kw">summary</span><span class="dt">&gt;</span>Disclosure 2<span class="dt">&lt;/</span><span class="kw">summary</span><span class="dt">&gt;</span></span>
<span id="cb34-5"><a aria-hidden="true" href="#cb34-5" tabindex="-1"></a> Disclosure 2 contents</span>
<span id="cb34-6"><a aria-hidden="true" href="#cb34-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">details</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>Compromising UX just to avoid JavaScript is bad development. But
sometimes it’s possible to achieve an equal (or better!) quality of UX
while allowing for a simpler and more robust implementation by changing
the design.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">A Dynamic Archive UI</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_ui_requirements">UI Requirements</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_beginning_our_implementation">Beginning Our Implementation</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_adding_the_archiving_endpoint">Adding the Archiving
Endpoint</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_conditionally_rendering_a_progress_ui">Conditionally Rendering
A Progress UI</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_polling">Polling</a>
<ul>
<li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_using_polling_to_update_the_archive_ui">Using Polling To Update
The Archive UI</a>
<ul>
<li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_adding_the_progress_bar_ui">Adding The Progress Bar UI</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_downloading_the_result">Downloading The Result</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_downloading_the_completed_archive">Downloading The Completed
Archive</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_smoothing_things_out_animations_in_htmx">Smoothing Things Out:
Animations in Htmx</a>
<ul>
<li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_the_settling_step_in_htmx">The “Settling” Step in Htmx</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_our_smoothing_solution">Our Smoothing Solution</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_dismissing_the_download_ui">Dismissing The Download UI</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_an_alternative_ux_auto_download">An Alternative UX:
Auto-Download</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#_a_dynamic_archive_ui_complete">A Dynamic Archive UI:
Complete</a>
</li><li>
<a href="https://hypermedia.systems/a-dynamic-archive-ui/#html-note-title">HTML Notes: Markdown soup</a>
</li></ul>
</details>
<div class="division-content">
<p>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.</p>
<p>Let’s look at how we can add a final significant feature to
Contact.app: downloading an archive of all the contacts.</p>
<p>From a hypermedia perspective, downloading a file isn’t exactly
rocket science: using the HTTP <code>Content-Disposition</code> response
header, we can easily tell the browser to download and save a file to
the local computer.</p>
<p>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.</p>
<p>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
<em class="test">second</em> archive request. Not good.</p>
<p>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:</p>
<ul>
<li><p>When the user triggers the action, block until it is complete and
then respond with the result.</p></li>
<li><p>Begin the action and return immediately, showing some sort of UI
indicating that things are in progress.</p></li>
</ul>
<p>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.</p>
<p>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.</p>
<p>But the question is, what do you respond <em class="test">with</em>? The job
probably isn’t complete yet, so you can’t provide a link to the
results.</p>
<p>We have seen a few different “simple” approaches in this scenario in
various web applications:</p>
<ul>
<li><p>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.</p></li>
<li><p>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.</p></li>
<li><p>Let the user know that the process has started and automatically
refresh the page every few seconds using some JavaScript.</p></li>
</ul>
<p>All of these will work, but none of them is a great user
experience.</p>
<p>What we’d <em class="test">really</em> 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.</p>
<p>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 <em class="test">that</em> much
code, and we will be able to achieve the user experience we want for
this archiving feature.</p>
<h2 id="_ui_requirements">UI Requirements</h2>
<p>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.</p>
<p>In order to actually do the archiving, we are going to use a python
class, <code>Archiver</code>, that implements all the functionality that
we need. As with the <code>Contact</code> class, we aren’t going to go
into the implementation details of <code>Archiver</code>, 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.</p>
<p><code>Archiver</code> gives us the following methods to work
with:</p>
<ul>
<li><p><code>status()</code> - A string representing the status of the
download, either <code>Waiting</code>, <code>Running</code> or
<code>Complete</code></p></li>
<li><p><code>progress()</code> - A number between 0 and 1, indicating
how much progress the archive job has made</p></li>
<li><p><code>run()</code> - Starts a new archive job (if the current
status is <code>Waiting</code>)</p></li>
<li><p><code>reset()</code> - Cancels the current archive job, if any,
and resets to the “Waiting” state</p></li>
<li><p><code>archive_file()</code> - The path to the archive file that
has been created on the server, so we can send it to the client</p></li>
<li><p><code>get()</code> - A class method that lets us get the Archiver
for the current user</p></li>
</ul>
<p>A fairly uncomplicated API.</p>
<p>The only somewhat tricky aspect to the whole API is that the
<code>run()</code> method is <em class="test">non-blocking</em>. This means that it
does not <em class="test">immediately</em> 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 <code>run()</code> 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.</p>
<h2 id="_beginning_our_implementation">Beginning Our Implementation</h2>
<p>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.</p>
<p>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.</p>
<p>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
<code>archive_ui.html</code>.</p>
<p>Also note that we are going to want to replace the entire download UI
in multiple cases:</p>
<ul>
<li><p>When we start the download, we will want to replace the button
with a progress bar.</p></li>
<li><p>As the archive process proceeds, we will want to replace/update
the progress bar.</p></li>
<li><p>When the archive process completes, we will want to replace the
progress bar with a download link.</p></li>
</ul>
<p>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 <code>div</code> tag, and
then use that <code>div</code> as the target for all our operations.</p>
<p>Here is the start of the template for our new archive user
interface:</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"archive-ui"</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"this"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a> hx-swap="outerHTML"&gt; <span class="er">&lt;</span>2&gt;</span>
<span id="cb1-4"><a aria-hidden="true" href="#cb1-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our initial archive UI template</p></figcaption>
</figure>
<ol>
<li><p>This div will be the target for all elements within it.</p></li>
<li><p>Replace the entire div every time using
<code>outerHTML</code>.</p></li>
</ol>
<p>Next, lets add the “Download Contact Archive” button to the
<code>div</code> that will kick off the archive-then-download process.
We’ll use a <code>POST</code> to the path <code>/contacts/archive</code>
to trigger the start of the archiving process:</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"archive-ui"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"this"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span><span class="dt">&gt;</span></span>
<span id="cb2-2"><a aria-hidden="true" href="#cb2-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb2-3"><a aria-hidden="true" href="#cb2-3" tabindex="-1"></a> Download Contact Archive</span>
<span id="cb2-4"><a aria-hidden="true" href="#cb2-4" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb2-5"><a aria-hidden="true" href="#cb2-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding the archive button</p></figcaption>
</figure>
<ol>
<li><p>This button will issue a <code>POST</code> to
<code>/contacts/archive</code>.</p></li>
</ol>
<p>Finally, let’s include this new template in our main
<code>index.html</code> template, above the contacts table:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a>{% block content %}</span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> {% include 'archive_ui.html' %} <span class="er">&lt;</span>1&gt;</span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> method</span><span class="op">=</span><span class="st">"get"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"tool-bar"</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Our initial archive UI template</p></figcaption>
</figure>
<ol>
<li><p>This template will now be included in the main template.</p></li>
</ol>
<p>With that done, we now have a button showing up in our web
application to get the download going. Since the enclosing
<code>div</code> has an <code>hx-target="this"</code> on it, the button
will inherit that target and replace that enclosing <code>div</code>
with whatever HTML comes back from the <code>POST</code> to
<code>/contacts/archive</code>.</p>
<h2 id="_adding_the_archiving_endpoint">Adding the Archiving
Endpoint</h2>
<p>Our next step is to handle the <code>POST</code> that our button is
making. We want to get the <code>Archiver</code> for the current user
and invoke the <code>run()</code> method on it. This will start the
archive process running. Then we will render some new content indicating
that the process is running.</p>
<p>To do that, we want to reuse the <code>archive_ui</code> template to
handle rendering the archive UI for both states, when the archiver is
“Waiting” and when it is “Running.” (We will handle the “Complete” state
in a bit).</p>
<p>This is a very common pattern: we put all the different potential UIs
for a given chunk of the user interface into a single template, and
conditionally render the appropriate interface. By keeping everything in
one file, it makes it much easier for other developers (or for us, if we
come back after a while!) to understand exactly how the UI works on the
client side.</p>
<p>Since we are going to conditionally render different user interfaces
based on the state of the archiver, we will need to pass the archiver
out to the template as a parameter. So, again: we need to invoke
<code>run()</code> on the archiver in our controller and then pass the
archiver along to the template, so it can render the UI appropriate for
the current status of the archive process.</p>
<p>Here is what the code looks like:</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/archive"</span>, methods<span class="op">=</span>[<span class="st">"POST"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a><span class="kw">def</span> start_archive():</span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a> archiver <span class="op">=</span> Archiver.get() <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb4-4"><a aria-hidden="true" href="#cb4-4" tabindex="-1"></a> archiver.run() <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb4-5"><a aria-hidden="true" href="#cb4-5" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"archive_ui.html"</span>, archiver<span class="op">=</span>archiver) <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Server-side code to start the archive
process</p></figcaption>
</figure>
<ol>
<li><p>Handle <code>POST</code> to
<code>/contacts/archive</code>.</p></li>
<li><p>Look up the Archiver.</p></li>
<li><p>Invoke the non-blocking <code>run()</code> method on it.</p></li>
<li><p>Render the <code>archive_ui.html</code> template, passing in the
archiver.</p></li>
</ol>
<h2 id="_conditionally_rendering_a_progress_ui">Conditionally Rendering
A Progress UI</h2>
<p>Now let’s turn our attention to updating our archiving UI by setting
<code>archive_ui.html</code> to conditionally render different content
depending on the state of the archive process.</p>
<p>Recall that the archiver has a <code>status()</code> method. When we
pass the archiver through as a variable to the template, we can consult
this <code>status()</code> method to see the status of the archive
process.</p>
<p>If the archiver has the status <code>Waiting</code>, we want to
render the “Download Contact Archive” button. If the status is
<code>Running</code>, we want to render a message indicating that
progress is happening. Let’s update our template code to do just
that:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"archive-ui"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"this"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span><span class="dt">&gt;</span></span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a> {% if archiver.status() == "Waiting" %} <span class="er">&lt;</span>1&gt;</span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="dt">&gt;</span></span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> Download Contact Archive</span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb5-6"><a aria-hidden="true" href="#cb5-6" tabindex="-1"></a> {% elif archiver.status() == "Running" %} <span class="er">&lt;</span>2&gt;</span>
<span id="cb5-7"><a aria-hidden="true" href="#cb5-7" tabindex="-1"></a> Running... <span class="er">&lt;</span>3&gt;</span>
<span id="cb5-8"><a aria-hidden="true" href="#cb5-8" tabindex="-1"></a> {% endif %}</span>
<span id="cb5-9"><a aria-hidden="true" href="#cb5-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding conditional rendering</p></figcaption>
</figure>
<ol>
<li><p>Only render the archive button if the status is
“Waiting.”</p></li>
<li><p>Render different content when status is “Running.”</p></li>
<li><p>For now, just some text saying the process is running.</p></li>
</ol>
<p>OK, great, we have some conditional logic in our template view, and
the server-side logic to support kicking off the archive process. We
don’t have a progress bar yet, but we’ll get there! Let’s see how this
works as it stands, and refresh the main page of our application…​</p>
<figure>
<pre><code>UndefinedError
jinja2.exceptions.UndefinedError: 'archiver' is undefined
</code></pre>
<figcaption><p>Something Went Wrong</p></figcaption>
</figure>
<p>Ouch!</p>
<p>We get an error message right out of the box. Why? Ah, we are
including the <code>archive_ui.html</code> in the
<code>index.html</code> template, but now the
<code>archive_ui.html</code> template expects the archiver to be passed
through to it, so it can conditionally render the correct UI.</p>
<p>That’s an easy fix: we just need to pass the archiver through when we
render the <code>index.html</code> template as well:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts"</span>)</span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a><span class="kw">def</span> contacts():</span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a> search <span class="op">=</span> request.args.get(<span class="st">"q"</span>)</span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a> <span class="cf">if</span> search <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb7-5"><a aria-hidden="true" href="#cb7-5" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.search(search)</span>
<span id="cb7-6"><a aria-hidden="true" href="#cb7-6" tabindex="-1"></a> <span class="cf">if</span> request.headers.get(<span class="st">'HX-Trigger'</span>) <span class="op">==</span> <span class="st">'search'</span>:</span>
<span id="cb7-7"><a aria-hidden="true" href="#cb7-7" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"rows.html"</span>, contacts<span class="op">=</span>contacts_set)</span>
<span id="cb7-8"><a aria-hidden="true" href="#cb7-8" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb7-9"><a aria-hidden="true" href="#cb7-9" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>()</span>
<span id="cb7-10"><a aria-hidden="true" href="#cb7-10" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>,</span>
<span id="cb7-11"><a aria-hidden="true" href="#cb7-11" tabindex="-1"></a> contacts<span class="op">=</span>contacts_set, archiver<span class="op">=</span>Archiver.get()) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Including the archiver when we render
index.html</p></figcaption>
</figure>
<ol>
<li><p>Pass through archiver to the main template</p></li>
</ol>
<p>Now with that done, we can load up the page. And, sure enough, we can
see the “Download Contact Archive” button.</p>
<p>When we click on it, the button is replaced with the content
“Running…​”, and we can see in our development console on the server-side
that the job is indeed getting kicked off properly.</p>
<h2 id="_polling">Polling</h2>
<p>That’s definitely progress, but we don’t exactly have the best
progress indicator here: just some static text telling the user that the
process is running.</p>
<p>We want to make the content update as the process makes progress and,
ideally, show a progress bar indicating how far along it is. How can we
do that in htmx using plain old hypermedia?</p>
<p>The technique we want to use here is called “polling”, where we issue
a request on an interval and update the UI based on the new state of the
server.</p>
<div id="sidebar">
<div>
<div>
<p><strong>Polling? Really?</strong></p>
</div>
<div>
<p>Polling has a bit of a bad rap, and it isn’t the sexiest technique in
the world: today developers might look at a more advanced technique like
WebSockets or Server Sent Events (SSE) to address this situation.</p>
<p>But, say what one will, polling <em class="test">works</em> and it is drop-dead
simple. You need to be careful not to overwhelm your system with polling
requests, but, with a bit of care, you can create a reliable, passively
updated component in your UI using it.</p>
</div>
</div>
</div>
<p>Htmx offers two types of polling. The first is “fixed rate polling”,
which uses a special <code>hx-trigger</code> syntax to indicate that
something should be polled on a fixed interval.</p>
<p>Here is an example:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/messages"</span><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"every 3s"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Fixed interval polling</p></figcaption>
</figure>
<ol>
<li><p>Trigger a <code>GET</code> to <code>/messages</code> every three
seconds.</p></li>
</ol>
<p>This works great in situations when you want to poll indefinitely,
for example if you want to constantly poll for new messages to display
to the user. However, fixed rate polling isn’t ideal when you have a
definite process after which you want to stop polling: it keeps polling
forever, until the element it is on is removed from the DOM.</p>
<p>In our case, we have a definite process with an ending to it. So, it
will be better to use the second polling technique, known as “load
polling.” In load polling, we take advantage of the fact that htmx
triggers a <code>load</code> event when content is loaded into the DOM.
We can create a trigger on this <code>load</code> event, and add a bit
of a delay so that the request doesn’t trigger immediately.</p>
<p>With this, we can conditionally render the <code>hx-trigger</code> on
every request: when a process has completed we simply do not include the
<code>load</code> trigger, and the load polling stops. This offers a
nice and simple way to poll until a definite process finishes.</p>
<h3 id="_using_polling_to_update_the_archive_ui">Using Polling To Update
The Archive UI</h3>
<p>Let’s use load polling to update our UI as the archiver makes
progress. To show the progress, let’s use a CSS-based progress bar,
taking advantage of the <code>progress()</code> method which returns a
number between 0 and 1 indicating how close the archive process is to
completion.</p>
<p>Here is the snippet of HTML we will use:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress"</span><span class="dt">&gt;</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress-bar"</span></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a><span class="ot"> style</span><span class="op">=</span><span class="st">"width:{{ archiver.progress() * 100 }}%"</span><span class="dt">&gt;&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb9-4"><a aria-hidden="true" href="#cb9-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A CSS-based progress bar</p></figcaption>
</figure>
<ol>
<li><p>The width of the inner element corresponds to the
progress.</p></li>
</ol>
<p>This CSS-based progress bar has two components: an outer
<code>div</code> that provides the wire frame for the progress bar, and
an inner <code>div</code> that is the actual progress bar indicator. We
set the width of the inner progress bar to some percentage (note we need
to multiply the <code>progress()</code> result by 100 to get a
percentage) and that will make the progress indicator the appropriate
width within the parent div.</p>
<div id="sidebar">
<div>
<div>
<p><strong>What About The <span id="progress"></span>
Element?</strong></p>
</div>
<div>
<p>We are perhaps dipping our toes into the “div soup” here, using a
<code>div</code> tag when there is a perfectly good HTML5 tag, the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"><code>progress</code></a>
element, that is designed specifically for showing, well, progress.</p>
<p>We decided not to use the <code>progress</code> element for this
example because we want our progress bar to update smoothly, and we will
need to use a CSS technique not available for the <code>progress</code>
element to make that happen. That’s unfortunate, but sometimes we have
to play with the cards we are dealt.</p>
<p>We will, however, use the proper <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/roles/progressbar_role">progress
bar roles</a> to make our <code>div</code>-based progress bar play well
with assistive technologies.</p>
</div>
</div>
</div>
<p>Let’s update our progress bar to have the proper ARIA roles and
values:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress"</span><span class="dt">&gt;</span></span>
<span id="cb10-2"><a aria-hidden="true" href="#cb10-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress-bar"</span></span>
<span id="cb10-3"><a aria-hidden="true" href="#cb10-3" tabindex="-1"></a><span class="ot"> role</span><span class="op">=</span><span class="st">"progressbar"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb10-4"><a aria-hidden="true" href="#cb10-4" tabindex="-1"></a> aria-valuenow="{{ archiver.progress() * 100 }}" <span class="er">&lt;</span>2&gt;</span>
<span id="cb10-5"><a aria-hidden="true" href="#cb10-5" tabindex="-1"></a> style="width:{{ archiver.progress() * 100 }}%"&gt;<span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb10-6"><a aria-hidden="true" href="#cb10-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A CSS-based progress bar</p></figcaption>
</figure>
<ol>
<li><p>This element will act as a progress bar</p></li>
<li><p>The progress will be the percentage completeness of the archiver,
with 100 indicating fully complete</p></li>
</ol>
<p>Finally, for completeness, here is the CSS we’ll use for this
progress bar:</p>
<figure id="lst:progress-bar-css">
<div class="sourceCode" id="cb11"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="fu">.progress</span> {</span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a> <span class="kw">height</span><span class="ch">:</span> <span class="dv">20</span><span class="dt">px</span><span class="op">;</span></span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> <span class="kw">margin-bottom</span><span class="ch">:</span> <span class="dv">20</span><span class="dt">px</span><span class="op">;</span></span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a> <span class="kw">overflow</span><span class="ch">:</span> <span class="dv">hidden</span><span class="op">;</span></span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#f5f5f5</span><span class="op">;</span></span>
<span id="cb11-6"><a aria-hidden="true" href="#cb11-6" tabindex="-1"></a> <span class="kw">border-radius</span><span class="ch">:</span> <span class="dv">4</span><span class="dt">px</span><span class="op">;</span></span>
<span id="cb11-7"><a aria-hidden="true" href="#cb11-7" tabindex="-1"></a> <span class="kw">box-shadow</span><span class="ch">:</span> <span class="dv">inset</span> <span class="dv">0</span> <span class="dv">1</span><span class="dt">px</span> <span class="dv">2</span><span class="dt">px</span> <span class="fu">rgba(</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">.1</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb11-8"><a aria-hidden="true" href="#cb11-8" tabindex="-1"></a>}</span>
<span id="cb11-9"><a aria-hidden="true" href="#cb11-9" tabindex="-1"></a></span>
<span id="cb11-10"><a aria-hidden="true" href="#cb11-10" tabindex="-1"></a><span class="fu">.progress-bar</span> {</span>
<span id="cb11-11"><a aria-hidden="true" href="#cb11-11" tabindex="-1"></a> <span class="kw">float</span><span class="ch">:</span> <span class="dv">left</span><span class="op">;</span></span>
<span id="cb11-12"><a aria-hidden="true" href="#cb11-12" tabindex="-1"></a> <span class="kw">width</span><span class="ch">:</span> <span class="dv">0</span><span class="dt">%</span><span class="op">;</span></span>
<span id="cb11-13"><a aria-hidden="true" href="#cb11-13" tabindex="-1"></a> <span class="kw">height</span><span class="ch">:</span> <span class="dv">100</span><span class="dt">%</span><span class="op">;</span></span>
<span id="cb11-14"><a aria-hidden="true" href="#cb11-14" tabindex="-1"></a> <span class="kw">font-size</span><span class="ch">:</span> <span class="dv">12</span><span class="dt">px</span><span class="op">;</span></span>
<span id="cb11-15"><a aria-hidden="true" href="#cb11-15" tabindex="-1"></a> <span class="kw">line-height</span><span class="ch">:</span> <span class="dv">20</span><span class="dt">px</span><span class="op">;</span></span>
<span id="cb11-16"><a aria-hidden="true" href="#cb11-16" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#fff</span><span class="op">;</span></span>
<span id="cb11-17"><a aria-hidden="true" href="#cb11-17" tabindex="-1"></a> <span class="kw">text-align</span><span class="ch">:</span> <span class="dv">center</span><span class="op">;</span></span>
<span id="cb11-18"><a aria-hidden="true" href="#cb11-18" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#337ab7</span><span class="op">;</span></span>
<span id="cb11-19"><a aria-hidden="true" href="#cb11-19" tabindex="-1"></a> <span class="kw">box-shadow</span><span class="ch">:</span> <span class="dv">inset</span> <span class="dv">0</span> <span class="dv">-1</span><span class="dt">px</span> <span class="dv">0</span> <span class="fu">rgba(</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">.15</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb11-20"><a aria-hidden="true" href="#cb11-20" tabindex="-1"></a> <span class="kw">transition</span><span class="ch">:</span> <span class="dv">width .6</span><span class="dt">s</span> <span class="dv">ease</span><span class="op">;</span></span>
<span id="cb11-21"><a aria-hidden="true" href="#cb11-21" tabindex="-1"></a>}</span></code></pre></div>
<figcaption><p>The CSS for our progress bar</p></figcaption>
</figure>
<figure>
<p><img src="https://hypermedia.systems/images/screenshot_progress_bar.png"/></p>
<figcaption><p>Our CSS-Based Progress Bar, as implemented in <span class="citation" data-cites="lst">[lst]</span>:progress-bar-css</p></figcaption>
</figure>
<h4 id="_adding_the_progress_bar_ui">Adding The Progress Bar UI</h4>
<p>Let’s add the code for our progress bar into our
<code>archive_ui.html</code> template for the case when the archiver is
running, and let’s update the copy to say “Creating Archive…​”:</p>
<figure>
<div class="sourceCode" id="cb12"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb12-1"><a aria-hidden="true" href="#cb12-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"archive-ui"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"this"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span><span class="dt">&gt;</span></span>
<span id="cb12-2"><a aria-hidden="true" href="#cb12-2" tabindex="-1"></a> {% if archiver.status() == "Waiting" %}</span>
<span id="cb12-3"><a aria-hidden="true" href="#cb12-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="dt">&gt;</span></span>
<span id="cb12-4"><a aria-hidden="true" href="#cb12-4" tabindex="-1"></a> Download Contact Archive</span>
<span id="cb12-5"><a aria-hidden="true" href="#cb12-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb12-6"><a aria-hidden="true" href="#cb12-6" tabindex="-1"></a> {% elif archiver.status() == "Running" %}</span>
<span id="cb12-7"><a aria-hidden="true" href="#cb12-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb12-8"><a aria-hidden="true" href="#cb12-8" tabindex="-1"></a> Creating Archive...</span>
<span id="cb12-9"><a aria-hidden="true" href="#cb12-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb12-10"><a aria-hidden="true" href="#cb12-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress-bar"</span><span class="ot"> role</span><span class="op">=</span><span class="st">"progressbar"</span></span>
<span id="cb12-11"><a aria-hidden="true" href="#cb12-11" tabindex="-1"></a><span class="ot"> aria-valuenow</span><span class="op">=</span><span class="st">"{{ archiver.progress() * 100}}"</span></span>
<span id="cb12-12"><a aria-hidden="true" href="#cb12-12" tabindex="-1"></a><span class="ot"> style</span><span class="op">=</span><span class="st">"width:{{ archiver.progress() * 100 }}%"</span><span class="dt">&gt;&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb12-13"><a aria-hidden="true" href="#cb12-13" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb12-14"><a aria-hidden="true" href="#cb12-14" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb12-15"><a aria-hidden="true" href="#cb12-15" tabindex="-1"></a> {% endif %}</span>
<span id="cb12-16"><a aria-hidden="true" href="#cb12-16" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Adding the progress bar</p></figcaption>
</figure>
<ol>
<li><p>Our shiny new progress bar</p></li>
</ol>
<p>Now when we click the “Download Contact Archive” button, we get the
progress bar. But it still doesn’t update because we haven’t implemented
load polling yet: it just sits there, at zero.</p>
<p>To get the progress bar updating dynamically, we’ll need to implement
load polling using <code>hx-trigger</code>. We can add this to pretty
much any element inside the conditional block for when the archiver is
running, so let’s add it to that <code>div</code> that is wrapping
around the “Creating Archive…​” text and the progress bar.</p>
<p>Let’s make it poll by issuing an HTTP <code>GET</code> to the same
path as the <code>POST</code>: <code>/contacts/archive</code>.</p>
<figure>
<div class="sourceCode" id="cb13"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb13-1"><a aria-hidden="true" href="#cb13-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"archive-ui"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"this"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span><span class="dt">&gt;</span></span>
<span id="cb13-2"><a aria-hidden="true" href="#cb13-2" tabindex="-1"></a> {% if archiver.status() == "Waiting" %}</span>
<span id="cb13-3"><a aria-hidden="true" href="#cb13-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="dt">&gt;</span></span>
<span id="cb13-4"><a aria-hidden="true" href="#cb13-4" tabindex="-1"></a> Download Contact Archive</span>
<span id="cb13-5"><a aria-hidden="true" href="#cb13-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb13-6"><a aria-hidden="true" href="#cb13-6" tabindex="-1"></a> {% elif archiver.status() == "Running" %}</span>
<span id="cb13-7"><a aria-hidden="true" href="#cb13-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"load delay:500ms"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb13-8"><a aria-hidden="true" href="#cb13-8" tabindex="-1"></a> Creating Archive...</span>
<span id="cb13-9"><a aria-hidden="true" href="#cb13-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress"</span><span class="ot"> </span><span class="dt">&gt;</span></span>
<span id="cb13-10"><a aria-hidden="true" href="#cb13-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress-bar"</span><span class="ot"> role</span><span class="op">=</span><span class="st">"progressbar"</span></span>
<span id="cb13-11"><a aria-hidden="true" href="#cb13-11" tabindex="-1"></a><span class="ot"> aria-valuenow</span><span class="op">=</span><span class="st">"{{ archiver.progress() * 100}}"</span></span>
<span id="cb13-12"><a aria-hidden="true" href="#cb13-12" tabindex="-1"></a><span class="ot"> style</span><span class="op">=</span><span class="st">"width:{{ archiver.progress() * 100 }}%"</span><span class="dt">&gt;&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb13-13"><a aria-hidden="true" href="#cb13-13" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb13-14"><a aria-hidden="true" href="#cb13-14" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb13-15"><a aria-hidden="true" href="#cb13-15" tabindex="-1"></a> {% endif %}</span>
<span id="cb13-16"><a aria-hidden="true" href="#cb13-16" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Implementing load polling</p></figcaption>
</figure>
<ol>
<li><p>Issue a <code>GET</code> to <code>/contacts/archive</code> 500
milliseconds after the content loads.</p></li>
</ol>
<p>When this <code>GET</code> is issued to
<code>/contacts/archive</code>, it is going to replace the
<code>div</code> with the id <code>archive-ui</code>, not just itself.
The <code>hx-target</code> attribute on the <code>div</code> with the id
<code>archive-ui</code> is <em class="test">inherited</em> by all child elements
within that <code>div</code>, so the children will all target that
outermost <code>div</code> in the <code>archive_ui.html</code> file.</p>
<p>Now we need to handle the <code>GET</code> to
<code>/contacts/archive</code> on the server. Thankfully, this is quite
easy: all we want to do is re-render <code>archive_ui.html</code> with
the archiver:</p>
<figure>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a aria-hidden="true" href="#cb14-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/archive"</span>, methods<span class="op">=</span>[<span class="st">"GET"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb14-2"><a aria-hidden="true" href="#cb14-2" tabindex="-1"></a><span class="kw">def</span> archive_status():</span>
<span id="cb14-3"><a aria-hidden="true" href="#cb14-3" tabindex="-1"></a> archiver <span class="op">=</span> Archiver.get()</span>
<span id="cb14-4"><a aria-hidden="true" href="#cb14-4" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"archive_ui.html"</span>, archiver<span class="op">=</span>archiver) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Handling progress updates</p></figcaption>
</figure>
<ol>
<li><p>handle <code>GET</code> to the <code>/contacts/archive</code>
path</p></li>
<li><p>just re-render the <code>archive_ui.html</code> template</p></li>
</ol>
<p>Like so much else with hypermedia, the code is very readable and not
complicated.</p>
<p>Now, when we click the “Download Contact Archive”, sure enough, we
get a progress bar that updates every 500 milliseconds. As the result of
the call to <code>archiver.progress()</code> incrementally updates from
0 to 1, the progress bar moves across the screen for us. Very cool!</p>
<h3 id="_downloading_the_result">Downloading The Result</h3>
<p>We have one final state to handle, the case when
<code>archiver.status()</code> is set to “Complete”, and there is a JSON
archive of the data ready to download. When the archiver is complete, we
can get the local JSON file on the server from the archiver via the
<code>archive_file()</code> call.</p>
<p>Let’s add another case to our if statement to handle the “Complete”
state, and, when the archive job is complete, lets render a link to a
new path, <code>/contacts/archive/file</code>, which will respond with
the archived JSON file. Here is the new code:</p>
<figure>
<div class="sourceCode" id="cb15"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb15-1"><a aria-hidden="true" href="#cb15-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"archive-ui"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"this"</span><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"outerHTML"</span><span class="dt">&gt;</span></span>
<span id="cb15-2"><a aria-hidden="true" href="#cb15-2" tabindex="-1"></a> {% if archiver.status() == "Waiting" %}</span>
<span id="cb15-3"><a aria-hidden="true" href="#cb15-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="dt">&gt;</span></span>
<span id="cb15-4"><a aria-hidden="true" href="#cb15-4" tabindex="-1"></a> Download Contact Archive</span>
<span id="cb15-5"><a aria-hidden="true" href="#cb15-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb15-6"><a aria-hidden="true" href="#cb15-6" tabindex="-1"></a> {% elif archiver.status() == "Running" %}</span>
<span id="cb15-7"><a aria-hidden="true" href="#cb15-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"load delay:500ms"</span><span class="dt">&gt;</span></span>
<span id="cb15-8"><a aria-hidden="true" href="#cb15-8" tabindex="-1"></a> Creating Archive...</span>
<span id="cb15-9"><a aria-hidden="true" href="#cb15-9" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress"</span><span class="ot"> </span><span class="dt">&gt;</span></span>
<span id="cb15-10"><a aria-hidden="true" href="#cb15-10" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress-bar"</span><span class="ot"> role</span><span class="op">=</span><span class="st">"progressbar"</span></span>
<span id="cb15-11"><a aria-hidden="true" href="#cb15-11" tabindex="-1"></a><span class="ot"> aria-valuenow</span><span class="op">=</span><span class="st">"{{ archiver.progress() * 100}}"</span></span>
<span id="cb15-12"><a aria-hidden="true" href="#cb15-12" tabindex="-1"></a><span class="ot"> style</span><span class="op">=</span><span class="st">"width:{{ archiver.progress() * 100 }}%"</span><span class="dt">&gt;&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb15-13"><a aria-hidden="true" href="#cb15-13" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb15-14"><a aria-hidden="true" href="#cb15-14" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb15-15"><a aria-hidden="true" href="#cb15-15" tabindex="-1"></a> {% elif archiver.status() == "Complete" %} <span class="er">&lt;</span>1&gt;</span>
<span id="cb15-16"><a aria-hidden="true" href="#cb15-16" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"false"</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/archive/file"</span><span class="dt">&gt;</span></span>
<span id="cb15-17"><a aria-hidden="true" href="#cb15-17" tabindex="-1"></a> Archive Ready! Click here to download. <span class="dv">&amp;downarrow;</span></span>
<span id="cb15-18"><a aria-hidden="true" href="#cb15-18" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb15-19"><a aria-hidden="true" href="#cb15-19" tabindex="-1"></a> {% endif %}</span>
<span id="cb15-20"><a aria-hidden="true" href="#cb15-20" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Rendering A Download Link When Archiving
Completes</p></figcaption>
</figure>
<ol>
<li><p>If the status is “Complete”, render a download link.</p></li>
<li><p>The link will issue a <code>GET</code> to
<code>/contacts/archive/file</code>.</p></li>
</ol>
<p>Note that the link has <code>hx-boost</code> set to
<code>false</code>. It has this so that the link will not inherit the
boost behavior that is present for other links and, thus, will not be
issued via AJAX. We want this “normal” link behavior because an AJAX
request cannot download a file directly, whereas a plain anchor tag
can.</p>
<h3 id="_downloading_the_completed_archive">Downloading The Completed
Archive</h3>
<p>The final step is to handle the <code>GET</code> request to
<code>/contacts/archive/file</code>. We want to send the file that the
archiver created down to the client. We are in luck: Flask has a
mechanism for sending a file as a downloaded response, the
<code>send_file()</code> method.</p>
<p>As you see in the code that follows, we pass three arguments to
<code>send_file()</code>: the path to the archive file that the archiver
created, the name of the file that we want the browser to create, and if
we want it sent “as an attachment.” This last argument tells Flask to
set the HTTP response header <code>Content-Disposition</code> to
<code>attachment</code> with the given filename; this is what triggers
the browser’s file-downloading behavior.</p>
<figure>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a aria-hidden="true" href="#cb16-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/archive/file"</span>, methods<span class="op">=</span>[<span class="st">"GET"</span>])</span>
<span id="cb16-2"><a aria-hidden="true" href="#cb16-2" tabindex="-1"></a><span class="kw">def</span> archive_content():</span>
<span id="cb16-3"><a aria-hidden="true" href="#cb16-3" tabindex="-1"></a> manager <span class="op">=</span> Archiver.get()</span>
<span id="cb16-4"><a aria-hidden="true" href="#cb16-4" tabindex="-1"></a> <span class="cf">return</span> send_file(</span>
<span id="cb16-5"><a aria-hidden="true" href="#cb16-5" tabindex="-1"></a> manager.archive_file(), <span class="st">"archive.json"</span>, as_attachment<span class="op">=</span><span class="va">True</span>) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Sending A File To The Client</p></figcaption>
</figure>
<ol>
<li><p>Send the file to the client via Flask’s <code>send_file()</code>
method.</p></li>
</ol>
<p>Perfect. Now we have an archive UI that is very slick. You click the
“Download Contacts Archive” button and a progress bar appears. When the
progress bar reaches 100%, it disappears and a link to download the
archive file appears. The user can then click on that link and download
their archive.</p>
<p>We’re offering a user experience that is much more user-friendly than
the common click-and-wait experience of many websites.</p>
<h2 id="_smoothing_things_out_animations_in_htmx">Smoothing Things Out:
Animations in Htmx</h2>
<p>As nice as this UI is, there is one minor annoyance: as the progress
bar updates it “jumps” from one position to the next. This feels a bit
like a full page refresh in web 1.0 style applications. Is there a way
we can fix this? (Obviously there is, this why we went with a
<code>div</code> rather than a <code>progress</code> element!)</p>
<p>Let’s walk through the cause of this visual problem and how we might
fix it. (If you’re in a hurry to get to an answer, feel free to jump
ahead to “our solution.”)</p>
<p>It turns out that there is a native HTML technology for smoothing out
changes on an element from one state to another: the CSS Transitions
API, the same one that we discussed in Chapter 4. Using CSS Transitions,
you can smoothly animate an element between different styling by using
the <code>transition</code> property.</p>
<p>If you look back at our CSS definition of the
<code>.progress-bar</code> class, you will see the following transition
definition: <code>transition: width .6s ease;</code>. This means that
when the width of the progress bar is changed from, say 20% to 30%, the
browser will animate over a period of .6 seconds using the “ease”
function (which has a nice accelerate/decelerate effect).</p>
<p>So why isn’t that transition being applied in our current UI? The
reason is that, in our example, htmx is <em class="test">replacing</em> the progress
bar with a new one every time it polls. It isn’t updating the width of
an <em class="test">existing</em> element. CSS transitions, unfortunately, only apply
when the properties of an existing element change inline, not when the
element is replaced.</p>
<p>This is a reason why pure HTML-based applications can feel jerky and
unpolished when compared with their SPA counterparts: it is hard to use
CSS transitions without some JavaScript.</p>
<p>But there is some good news: htmx has a way to utilize CSS
transitions even when it replaces content in the DOM.</p>
<h3 id="_the_settling_step_in_htmx">The “Settling” Step in Htmx</h3>
<p>When we discussed the htmx swap model in Chapter 4, we focused on the
classes that htmx adds and removes, but we skipped over the process of
“settling.” In htmx, settling involves several steps: when htmx is about
to replace a chunk of content, it looks through the new content and
finds all elements with an <code>id</code> on it. It then looks in the
<em class="test">existing</em> content for elements with the same
<code>id</code>.</p>
<p>If there is one, it does the following somewhat elaborate
shuffle:</p>
<ul>
<li><p>The <em class="test">new</em> content gets the attributes of the <em class="test">old</em>
content temporarily.</p></li>
<li><p>The new content is inserted.</p></li>
<li><p>After a small delay, the new content has its attributes reverted
to their actual values.</p></li>
</ul>
<p>So, what is this strange little dance supposed to achieve?</p>
<p>Well, if an element has a stable id between swaps, you can now write
CSS transitions between various states. Since the <em class="test">new</em> content
briefly has the <em class="test">old</em> attributes, the normal CSS transition
mechanism will kick in when the actual values are restored.</p>
<h3 id="_our_smoothing_solution">Our Smoothing Solution</h3>
<p>So, we arrive at our fix.</p>
<p>All we need to do is add a stable ID to our <code>progress-bar</code>
element.</p>
<figure>
<div class="sourceCode" id="cb17"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb17-1"><a aria-hidden="true" href="#cb17-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress"</span><span class="ot"> </span><span class="dt">&gt;</span></span>
<span id="cb17-2"><a aria-hidden="true" href="#cb17-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> id</span><span class="op">=</span><span class="st">"archive-progress"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"progress-bar"</span><span class="ot"> role</span><span class="op">=</span><span class="st">"progressbar"</span></span>
<span id="cb17-3"><a aria-hidden="true" href="#cb17-3" tabindex="-1"></a><span class="ot"> aria-valuenow</span><span class="op">=</span><span class="st">"{{ archiver.progress() * 100 }}"</span></span>
<span id="cb17-4"><a aria-hidden="true" href="#cb17-4" tabindex="-1"></a><span class="ot"> style</span><span class="op">=</span><span class="st">"width:{{ archiver.progress() * 100 }}%"</span><span class="dt">&gt;&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb17-5"><a aria-hidden="true" href="#cb17-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Smoothing things out</p></figcaption>
</figure>
<ol>
<li><p>The progress bar div now has a stable id across
requests.</p></li>
</ol>
<p>Despite the complicated mechanics going on behind the scenes in htmx,
the solution is as simple as adding a stable <code>id</code> attribute
to the element we want to animate.</p>
<p>Now, rather than jumping on every update, the progress bar should
smoothly move across the screen as it is updating, using the CSS
transition defined in our style sheet. The htmx swapping model allows us
to achieve this even though we are replacing the content with new
HTML.</p>
<p>And voila: we have a nice, smoothly animated progress bar for our
contact archiving feature. The result has the look and feel of a
JavaScript-based solution, but we did it with the simplicity of an
HTML-based approach.</p>
<p>Now that, dear reader, does spark joy.</p>
<h2 id="_dismissing_the_download_ui">Dismissing The Download UI</h2>
<p>Some users may change their mind, and decide not to download the
archive. They may never witness our glorious progress bar, but that’s
OK. We’re going to give these users a button to dismiss the download
link and return to the original export UI state.</p>
<p>To do this, we’ll add a button that issues a <code>DELETE</code> to
the path <code>/contacts/archive</code>, indicating that the current
archive can be removed or cleaned up.</p>
<p>We’ll add it after the download link, like so:</p>
<figure>
<div class="sourceCode" id="cb18"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb18-1"><a aria-hidden="true" href="#cb18-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"false"</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/archive/file"</span><span class="dt">&gt;</span></span>
<span id="cb18-2"><a aria-hidden="true" href="#cb18-2" tabindex="-1"></a> Archive Ready! Click here to download. <span class="dv">&amp;downarrow;</span></span>
<span id="cb18-3"><a aria-hidden="true" href="#cb18-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb18-4"><a aria-hidden="true" href="#cb18-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts/archive"</span><span class="dt">&gt;</span>Clear Download<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
<figcaption><p>Clearing the download</p></figcaption>
</figure>
<ol>
<li><p>A simple button that issues a <code>DELETE</code> to
<code>/contacts/archive</code>.</p></li>
</ol>
<p>Now the user has a button that they can click on to dismiss the
archive download link. But we will need to hook it up on the server
side. As usual, this is pretty straightforward: we create a new handler
for the <code>DELETE</code> HTTP Action, invoke the <code>reset()</code>
method on the archiver, and re-render the <code>archive_ui.html</code>
template.</p>
<p>Since this button is picking up the same <code>hx-target</code> and
<code>hx-swap</code> configuration as everything else, it “just
works.”</p>
<p>Here is the server-side code:</p>
<figure>
<div class="sourceCode" id="cb19"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb19-1"><a aria-hidden="true" href="#cb19-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/contacts/archive"</span>, methods<span class="op">=</span>[<span class="st">"DELETE"</span>])</span>
<span id="cb19-2"><a aria-hidden="true" href="#cb19-2" tabindex="-1"></a><span class="kw">def</span> reset_archive():</span>
<span id="cb19-3"><a aria-hidden="true" href="#cb19-3" tabindex="-1"></a> archiver <span class="op">=</span> Archiver.get()</span>
<span id="cb19-4"><a aria-hidden="true" href="#cb19-4" tabindex="-1"></a> archiver.reset() <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb19-5"><a aria-hidden="true" href="#cb19-5" tabindex="-1"></a> <span class="cf">return</span> render_template(<span class="st">"archive_ui.html"</span>, archiver<span class="op">=</span>archiver)</span></code></pre></div>
<figcaption><p>The handler to reset the download</p></figcaption>
</figure>
<ol>
<li><p>Call <code>reset()</code> on the archiver</p></li>
</ol>
<p>This looks pretty similar to our other handlers, doesn’t it?</p>
<p>Sure does! That’s the idea!</p>
<h2 id="_an_alternative_ux_auto_download">An Alternative UX:
Auto-Download</h2>
<p>While we prefer the current user experience for archiving contacts,
there are other alternatives. Currently, a progress bar shows the
progress of the process and, when it completes, the user is presented
with a link to actually download the file. Another pattern that we see
on the web is “auto-downloading”, where the file downloads immediately
without the user needing to click a link.</p>
<p>We can add this functionality quite easily to our application with
just a bit of scripting. We will discuss scripting in a
Hypermedia-Driven Application in more depth in Chapter 9, but, put
briefly: scripting is perfectly acceptable in a HDA, as long as it
doesn’t replace the core hypermedia mechanics of the application.</p>
<p>For our auto-download feature we will use <a href="https://hyperscript.org">_hyperscript</a>, our preferred scripting
option. JavaScript would also work here, and would be nearly as simple;
again, we’ll discuss scripting options in detail in Chapter 9.</p>
<p>All we need to do to implement the auto-download feature is the
following: when the download link renders, automatically click on the
link for the user.</p>
<p>The _hyperscript code reads almost the same as the previous sentence
(which is a major reason why we love hyperscript):</p>
<figure>
<div class="sourceCode" id="cb20"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb20-1"><a aria-hidden="true" href="#cb20-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> hx-boost</span><span class="op">=</span><span class="st">"false"</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/archive/file"</span></span>
<span id="cb20-2"><a aria-hidden="true" href="#cb20-2" tabindex="-1"></a><span class="ot"> _</span><span class="op">=</span><span class="st">"on load click() me"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb20-3"><a aria-hidden="true" href="#cb20-3" tabindex="-1"></a> Archive Downloading! Click here if the download does not start.</span>
<span id="cb20-4"><a aria-hidden="true" href="#cb20-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Auto-downloading</p></figcaption>
</figure>
<ol>
<li><p>A bit of _hyperscript to make the file auto-download.</p></li>
</ol>
<p>Crucially, the scripting here is simply <em class="test">enhancing</em> the
existing hypermedia, rather than replacing it with a non-hypermedia
request. This is hypermedia-friendly scripting, as we will cover in more
depth in a bit.</p>
<h2 id="_a_dynamic_archive_ui_complete">A Dynamic Archive UI:
Complete</h2>
<p>In this chapter we’ve managed to create a dynamic UI for our contact
archive functionality, with a progress bar and auto-downloading, and
we’ve done nearly all of it — with the exception of a small bit of
scripting for auto-download — in pure hypermedia. It took about 16 lines
of front end code and 16 lines of backend code to build the whole
thing.</p>
<p>HTML, with a bit of help from a hypermedia-oriented JavaScript
library such as htmx, can in fact be extremely powerful and
expressive.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Markdown soup</h2>
<p><em class="test">Markdown soup</em> is the lesser known sibling of
<code>&lt;div&gt;</code> soup. This is the result of web developers
limiting themselves to the set of elements that the Markdown language
provides shorthand for, even when these elements are incorrect. More
seriously, it’s important to be aware of the full power of our tools,
including HTML. Consider the following example of an IEEE-style
citation:</p>
<figure>
<div class="sourceCode" id="cb21"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb21-1"><a aria-hidden="true" href="#cb21-1" tabindex="-1"></a><span class="co">[</span><span class="ot">1</span><span class="co">]</span> C.H. Gross, A. Stepinski, and D. Akşimşek, &lt;1&gt;</span>
<span id="cb21-2"><a aria-hidden="true" href="#cb21-2" tabindex="-1"></a> _Hypermedia Systems_, &lt;2&gt;</span>
<span id="cb21-3"><a aria-hidden="true" href="#cb21-3" tabindex="-1"></a> Bozeman, MT, USA: Big Sky Software.</span>
<span id="cb21-4"><a aria-hidden="true" href="#cb21-4" tabindex="-1"></a> Available: <span class="ot">&lt;https://hypermedia.systems/&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>The reference number is written in brackets.</p></li>
<li><p>Underscores around the book title creates an &lt;em&gt;
element.</p></li>
</ol>
<p>Here, &lt;em&gt; is used because it’s the only Markdown element that
is presented in italics by default. This indicates that the book title
is being stressed, but the purpose is to mark it as the title of a work.
HTML has the <code>&lt;cite&gt;</code> element that’s intended for this
exact purpose.</p>
<p>Furthermore, even though this is a numbered list perfect for the
<code>&lt;ol&gt;</code> element, which Markdown supports, plain text is
used for the reference numbers instead. Why could this be? The IEEE
citation style requires that these numbers are presented in square
brackets. This could be achieved on an <code>&lt;ol&gt;</code> with CSS,
but Markdown doesn’t have a way to add a class to elements meaning the
square brackets would apply to all ordered lists.</p>
<p>Don’t shy away from using embedded HTML in Markdown. For larger
sites, also consider Markdown extensions.</p>
<figure>
<div class="sourceCode" id="cb22"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb22-1"><a aria-hidden="true" href="#cb22-1" tabindex="-1"></a>{.ieee-reference-list} &lt;1&gt;</span>
<span id="cb22-2"><a aria-hidden="true" href="#cb22-2" tabindex="-1"></a><span class="ss">1. </span>C.H. Gross, A. Stepinski, and D. Akşimşek, &lt;2&gt;</span>
<span id="cb22-3"><a aria-hidden="true" href="#cb22-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">cite</span><span class="dt">&gt;</span>Hypermedia Systems<span class="dt">&lt;/</span><span class="kw">cite</span><span class="dt">&gt;</span>, &lt;3&gt;</span>
<span id="cb22-4"><a aria-hidden="true" href="#cb22-4" tabindex="-1"></a> Bozeman, MT, USA: Big Sky Software.</span>
<span id="cb22-5"><a aria-hidden="true" href="#cb22-5" tabindex="-1"></a> Available: <span class="ot">&lt;https://hypermedia.systems/&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Many Markdown dialects let us add ids, classes and attributes
using curly braces.</p></li>
<li><p>We can now use the &lt;ol&gt; element, and create the brackets in
CSS.</p></li>
<li><p>We use <code>&lt;cite&gt;</code> to mark the title of the work
being cited (not the whole citation!)</p></li>
</ol>
<p>You can also use custom processors to produce extra-detailed HTML
instead of writing it by hand:</p>
<figure>
<div class="sourceCode" id="cb23"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb23-1"><a aria-hidden="true" href="#cb23-1" tabindex="-1"></a>{% reference_list %} &lt;1&gt;</span>
<span id="cb23-2"><a aria-hidden="true" href="#cb23-2" tabindex="-1"></a><span class="ot">[hypers2023]: </span>&lt;2&gt;</span>
<span id="cb23-3"><a aria-hidden="true" href="#cb23-3" tabindex="-1"></a> C.H. Gross, A. Stepinski, and D. Akşimşek, _Hypermedia Systems_,</span>
<span id="cb23-4"><a aria-hidden="true" href="#cb23-4" tabindex="-1"></a> Bozeman, MT, USA: Big Sky Software, 2023.</span>
<span id="cb23-5"><a aria-hidden="true" href="#cb23-5" tabindex="-1"></a> Available: <span class="ot">&lt;https://hypermedia.systems/&gt;</span></span>
<span id="cb23-6"><a aria-hidden="true" href="#cb23-6" tabindex="-1"></a>{% end %}</span></code></pre></div>
</figure>
<ol>
<li><p><code>reference_list</code> is a macro that will transform the
plain text to highly-detailed HTML.</p></li>
<li><p>A processor can also resolve identifiers, so we don’t have to
manually keep the reference list in order and the in-text citations in
sync.</p></li>
</ol>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Tricks Of The Htmx Masters</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#htmx-attributes">Htmx Attributes</a>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#hx-swap">hx-swap</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#hx-trigger">hx-trigger</a>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#trigger-filters">Trigger filters</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#synthetic-events">Synthetic events</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#_other_attributes">Other Attributes</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#events">Events</a>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#htmx-generated-events">Htmx-Generated Events</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#using-the-htmx-configrequest-event">Using the htmx:configRequest Event</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#canceling-a-request-using-htmx-abort">Canceling a Request Using htmx:abort</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#_server_generated_events">Server Generated Events</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#_http_requests_responses">HTTP Requests &amp; Responses</a>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#http-response-codes">HTTP Response Codes</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#_updating_other_content">Updating Other Content</a>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#_expanding_your_selection">Expanding Your Selection</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#out-of-band-swaps">Out of Band Swaps</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#events-1">Events</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#being-pragmatic">Being Pragmatic</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#debugging">Debugging</a>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#_logging_htmx_events">Logging Htmx Events</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#_monitoring_events_in_chrome">Monitoring Events in Chrome</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#security-considerations">Security Considerations</a>
<ul>
<li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#content-security-policies---htmx">Content Security Policies &amp; Htmx</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#configuring">Configuring</a>
</li><li>
<a href="https://hypermedia.systems/tricks-of-the-htmx-masters/#html-note-title">HTML Notes: Semantic HTML</a>
</li></ul>
</details>
<div class="division-content">
<p>In this chapter we are going to look deeper into the htmx toolkit.
We’ve accomplished quite a bit with what we’ve learned so far. Still,
when you are developing Hypermedia-Driven Applications, there will be
times when you need to reach for additional options and techniques.</p>
<p>We will go over the more advanced attributes in htmx, as well as
expand on the advanced details of attributes we have already used.</p>
<p>Additionally, we will look at functionality that htmx offers beyond
simple HTML attributes: how htmx extends standard HTTP request and
responses, how htmx works with (and produces) events, and how to
approach situations where there isn’t a simple, single target on the
page to be updated.</p>
<p>Finally, we will take a look at practical considerations when doing
htmx development: how to debug htmx-based applications effectively,
security considerations you will need to take into account when working
with htmx, and how to configure the behavior of htmx.</p>
<p>With the features and techniques in this chapter, you will be able to
pull off extremely sophisticated user interfaces using only htmx and
perhaps a small bit of hypermedia-friendly client-side scripting.</p>
<h2 id="htmx-attributes">Htmx Attributes</h2>
<p>Thus far we have used about fifteen different attributes from htmx in
our application. The most important ones have been:</p>
<dl>
<dt><code>hx-get</code>, <code>hx-post</code>, etc.</dt>
<dd>
<p>To specify the AJAX request an element should make</p>
</dd>
<dt><code>hx-trigger</code></dt>
<dd>
<p>To specify the event that triggers a request</p>
</dd>
<dt><code>hx-swap</code></dt>
<dd>
<p>To specify how to swap the returned HTML content into the DOM</p>
</dd>
<dt><code>hx-target</code></dt>
<dd>
<p>To specify where in the DOM to swap the returned HTML content</p>
</dd>
</dl>
<p>Two of these attributes, <code>hx-swap</code> and
<code>hx-trigger</code>, support a number of useful options for creating
more advanced Hypermedia-Driven Applications.</p>
<h3 id="hx-swap">hx-swap</h3>
<p>We’ll start with the <code>hx-swap</code> attribute. This is often
not included on elements that issue htmx-driven requests because its
default behavior — <code>innerHTML</code>, which swaps the inner HTML of
the element — tends to cover most use cases.</p>
<p>We earlier saw situations where we wanted to override the default
behavior and use <code>outerHTML</code>, for example. And, in Chapter 2,
we discussed some other swap options beyond these two,
<code>beforebegin</code>, <code>afterend</code>, etc.</p>
<p>In Chapter 5, we also looked at the <code>swap</code> delay modifier
for <code>hx-swap</code>, which allowed us to fade some content out
before it was removed from the DOM.</p>
<p>In addition to these, <code>hx-swap</code> offers further control
with the following modifiers:</p>
<dl>
<dt><code>settle</code></dt>
<dd>
<p>Like <code>swap</code>, this allows you to apply a specific delay
between when the content has been swapped into the DOM and when its
attributes are “settled”, that is, updated from their old values (if
any) to their new values. This can give you fine-grained control over
CSS transitions.</p>
</dd>
<dt><code>show</code></dt>
<dd>
<p>Allows you to specify an element that should be shown — that is,
scrolled into the viewport of the browser if necessary — when a request
is completed.</p>
</dd>
<dt><code>scroll</code></dt>
<dd>
<p>Allows you to specify a scrollable element (that is, an element with
scrollbars), that should be scrolled to the top or bottom when a request
is completed.</p>
</dd>
<dt><code>focus-scroll</code></dt>
<dd>
<p>Allows you to specify that htmx should scroll to the focused element
when a request completes. The default for this modifier is “false.”</p>
</dd>
</dl>
<p>So, for example, if we had a button that issued a <code>GET</code>
request, and we wished to scroll to the top of the <code>body</code>
element when the request completed, we would write the following
HTML:</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"#content-div"</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a><span class="ot"> hx-swap</span><span class="op">=</span><span class="st">"innerHTML show:body:top"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a> Get Contacts</span>
<span id="cb1-4"><a aria-hidden="true" href="#cb1-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Scrolling to the top of the page</p></figcaption>
</figure>
<ol>
<li><p>This tells htmx to show the top of the body after the swap
occurs.</p></li>
</ol>
<p>More details and examples can be found online in the
<code>hx-swap</code> <a href="https://htmx.org/attributes/hx-swap/">documentation</a>.</p>
<h3 id="hx-trigger">hx-trigger</h3>
<p>Like <code>hx-swap</code>, <code>hx-trigger</code> can often be
omitted when you are using htmx, because the default behavior is
typically what you want. Recall the default triggering events are
determined by an element’s type:</p>
<ul>
<li><p>Requests on <code>input</code>, <code>textarea</code> &amp;
<code>select</code> elements are triggered by the <code>change</code>
event.</p></li>
<li><p>Requests on <code>form</code> elements are triggered on the
<code>submit</code> event.</p></li>
<li><p>Requests on all other elements are triggered by the
<code>click</code> event.</p></li>
</ul>
<p>There are times, however, when you want a more elaborate trigger
specification. A classic example is the active search example we
implemented in Contact.app:</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span></span>
<span id="cb2-2"><a aria-hidden="true" href="#cb2-2" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ request.args.get('q') or '' }}"</span></span>
<span id="cb2-3"><a aria-hidden="true" href="#cb2-3" tabindex="-1"></a><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb2-4"><a aria-hidden="true" href="#cb2-4" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"search, keyup delay:200ms changed"</span><span class="dt">/&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
<figcaption><p>The active search input</p></figcaption>
</figure>
<ol>
<li><p>An elaborate trigger specification.</p></li>
</ol>
<p>This example took advantage of two modifiers available for the
<code>hx-trigger</code> attribute:</p>
<dl>
<dt><code>delay</code></dt>
<dd>
<p>Allows you to specify a delay to wait before a request is issued. If
the event occurs again, the first event is discarded and the timer
resets. This allows you to “debounce” requests.</p>
</dd>
<dt><code>changed</code></dt>
<dd>
<p>Allows you to specify that a request should only be issued when the
<code>value</code> property of the given element has changed.</p>
</dd>
</dl>
<p><code>hx-trigger</code> has several additional modifiers. This makes
sense, because events are fairly complex and we want to be able to take
advantage of all the power they offer. We will discuss events in more
detail below.</p>
<p>Here are the other modifiers available on
<code>hx-trigger</code>:</p>
<dl>
<dt><code>once</code></dt>
<dd>
<p>The given event will only trigger a request once.</p>
</dd>
<dt><code>throttle</code></dt>
<dd>
<p>Allows you to throttle events, only issuing them once every certain
interval. This is different than <code>delay</code> in that the first
event will trigger immediately, but any following events will not
trigger until the throttle time period has elapsed.</p>
</dd>
<dt><code>from</code></dt>
<dd>
<p>A CSS selector that allows you to pick another element to listen for
events on. We will see an example of this used later in the chapter.</p>
</dd>
<dt><code>target</code></dt>
<dd>
<p>A CSS selector that allows you to filter events to only those that
occur directly on a given element. In the DOM, events “bubble” to their
ancestor elements, so a <code>click</code> event on a button will also
trigger a <code>click</code> event on an enclosing <code>div</code>, all
the way up to the <code>body</code> element. Sometimes you want to
specify an event directly on a given element, and this attribute allows
you to do that.</p>
</dd>
<dt><code>consume</code></dt>
<dd>
<p>If this option is set to <code>true</code>, the triggering event will
be cancelled and not propagate to ancestor elements.</p>
</dd>
<dt><code>queue</code></dt>
<dd>
<p>This option allows you to specify how events are queued in htmx. By
default, when htmx receives a triggering event, it will issue a request
and start an event queue. If the request is still in flight when another
event is received, it will queue the event and, when the request
finishes, trigger a new request. By default, it only keeps the last
event it receives, but you can modify that behavior using this option:
for example, you can set it to <code>none</code> and ignore all
triggering events that occur during a request.</p>
</dd>
</dl>
<h4 id="trigger-filters">Trigger filters</h4>
<p>The <code>hx-trigger</code> attribute also allows you to specify a
<em class="test">filter</em> for events by using square brackets enclosing a
JavaScript expression after the event name.</p>
<p>Let’s say you have a complex situation where contacts should only be
retrievable in certain situations. You have a JavaScript function,
<code>contactRetrievalEnabled()</code> that returns a boolean,
<code>true</code> if contacts can be retrieved and <code>false</code>
otherwise. How could you use this function to place a gate on a button
that issues a request to <code>/contacts</code>?</p>
<p>To do this using an event filter in htmx, you would write the
following HTML:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">script</span><span class="dt">&gt;</span></span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> <span class="kw">function</span> <span class="fu">contactRetrievalEnabled</span>() {</span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a> <span class="co">// code to test if contact retrieval is enabled</span></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a> <span class="op">...</span></span>
<span id="cb3-5"><a aria-hidden="true" href="#cb3-5" tabindex="-1"></a> }</span>
<span id="cb3-6"><a aria-hidden="true" href="#cb3-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">script</span><span class="dt">&gt;</span></span>
<span id="cb3-7"><a aria-hidden="true" href="#cb3-7" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span></span>
<span id="cb3-8"><a aria-hidden="true" href="#cb3-8" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"click[contactRetrievalEnabled()]"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb3-9"><a aria-hidden="true" href="#cb3-9" tabindex="-1"></a> Get Contacts</span>
<span id="cb3-10"><a aria-hidden="true" href="#cb3-10" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The active search input</p></figcaption>
</figure>
<ol>
<li><p>A request is issued on click only when
<code>contactRetrievalEnabled()</code> returns
<code>true</code>.</p></li>
</ol>
<p>The button will not issue a request if
<code>contactRetrievalEnabled()</code> returns false, allowing you to
dynamically control when the request will be made. There are common
situations that call for an event trigger, when you only want to issue a
request under specific circumstances:</p>
<ul>
<li><p>if a certain element has focus</p></li>
<li><p>if a given form is valid</p></li>
<li><p>if a set of inputs have specific values</p></li>
</ul>
<p>Using event filters, you can use whatever logic you’d like to filter
requests by htmx.</p>
<h4 id="synthetic-events">Synthetic events</h4>
<p>In addition to these modifiers, <code>hx-trigger</code> offers a few
“synthetic” events, that is events that are not part of the regular DOM
API. We have already seen <code>load</code> and <code>revealed</code> in
our lazy loading and infinite scroll examples, but htmx also gives you
an <code>intersect</code> event that triggers when an element intersects
its viewport.</p>
<p>This synthetic event uses the modern Intersection Observer API, which
you can read more about at <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">MDN</a>.</p>
<p>Intersection gives you fine-grained control over exactly when a
request should be triggered. For example, you can set a threshold and
specify that the request be issued only when an element is 50%
visible.</p>
<p>The <code>hx-trigger</code> attribute certainly is the most complex
in htmx. More details and examples can be found in its <a href="https://htmx.org/attributes/hx-trigger/">documentation</a>.</p>
<h3 id="_other_attributes">Other Attributes</h3>
<p>Htmx offers many other less commonly used attributes for fine-tuning
the behavior of your Hypermedia-Driven Application.</p>
<p>Here are some of the most useful ones:</p>
<dl>
<dt>hx-push-url</dt>
<dd>
<p>“Pushes” the request URL (or some other value) into the navigation
bar.</p>
</dd>
<dt>hx-preserve</dt>
<dd>
<p>Preserves a bit of the DOM between requests; the original content
will be kept, regardless of what is returned.</p>
</dd>
<dt>hx-sync</dt>
<dd>
<p>Synchronized requests between two or more elements.</p>
</dd>
<dt>hx-disable</dt>
<dd>
<p>Disables htmx behavior on this element and any children. We will come
back to this when we discuss the topic of security.</p>
</dd>
</dl>
<p>Let’s take a look at <code>hx-sync</code>, which allows us to
synchronize AJAX requests between two or more elements. Consider a
simple case where we have two buttons that both target the same element
on the screen:</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span></span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a> Get Contacts</span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb4-4"><a aria-hidden="true" href="#cb4-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/settings"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span></span>
<span id="cb4-5"><a aria-hidden="true" href="#cb4-5" tabindex="-1"></a> Get Settings</span>
<span id="cb4-6"><a aria-hidden="true" href="#cb4-6" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Two competing buttons</p></figcaption>
</figure>
<p>This is fine and will work, but what if a user clicks the “Get
Contacts” button and then the request takes a while to respond? And, in
the meantime the user clicks the “Get Settings” button? In this case we
would have two requests in flight at the same time.</p>
<p>If the <code>/settings</code> request finished first and displayed
the user’s setting information, they might be very surprised if they
began making changes and then, suddenly, the <code>/contacts</code>
request finished and replaced the entire body with the contacts
instead!</p>
<p>To deal with this situation, we might consider using an
<code>hx-indicator</code> to alert the user that something is going on,
making it less likely that they click the second button. But if we
really want to guarantee that there is only one request at a time issued
between these two buttons, the right thing to do is to use the
<code>hx-sync</code> attribute. Let’s enclose both buttons in a
<code>div</code> and eliminate the redundant <code>hx-target</code>
specification by hoisting the attribute up to that <code>div</code>. We
can then use <code>hx-sync</code> on that div to coordinate requests
between the two buttons.</p>
<p>Here is our updated code:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a> hx-sync="this"&gt; <span class="er">&lt;</span>2&gt;</span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="dt">&gt;</span></span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> Get Contacts</span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb5-6"><a aria-hidden="true" href="#cb5-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/settings"</span><span class="dt">&gt;</span></span>
<span id="cb5-7"><a aria-hidden="true" href="#cb5-7" tabindex="-1"></a> Get Settings</span>
<span id="cb5-8"><a aria-hidden="true" href="#cb5-8" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb5-9"><a aria-hidden="true" href="#cb5-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Syncing two buttons</p></figcaption>
</figure>
<ol>
<li><p>Hoist the duplicate <code>hx-target</code> attributes to the
parent <code>div</code>.</p></li>
<li><p>Synchronize on the parent <code>div</code>.</p></li>
</ol>
<p>By placing the <code>hx-sync</code> attribute on the <code>div</code>
with the value <code>this</code>, we are saying “Synchronize all htmx
requests that occur within this <code>div</code> element with one
another.” This means that if one button already has a request in flight,
other buttons within the <code>div</code> will not issue requests until
that has finished.</p>
<p>The <code>hx-sync</code> attribute supports a few different
strategies that allow you to, for example, replace an existing request
in flight, or queue requests with a particular queuing strategy. You can
find complete documentation, as well as examples, at the htmx.org page
for <a href="https://htmx.org/attributes/hx-sync/"><code>hx-sync</code></a>.</p>
<p>As you can see, htmx offers a lot of attribute-driven functionality
for more advanced Hypermedia-Driven Applications. A complete reference
for all htmx attributes can be found <a href="https://htmx.org/reference/#attributes">on the htmx
website</a>.</p>
<h2 id="events">Events</h2>
<p>Thus far we have worked with JavaScript events in htmx primarily via
the <code>hx-trigger</code> attribute. This attribute has proven to be a
powerful mechanism for driving our application using a declarative,
HTML-friendly syntax.</p>
<p>However, there is much more we can do with events. Events play a
crucial role both in the extension of HTML as a hypermedia, and, as
we’ll see, in hypermedia-friendly scripting. Events are the “glue” that
brings the DOM, HTML, htmx and scripting together. You might think of
the DOM as a sophisticated “event bus” for applications.</p>
<p>We can’t emphasize enough: to build advanced Hypermedia-Driven
Applications, it is worth the effort to learn about events <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events">in
depth</a>.</p>
<h3 id="htmx-generated-events">Htmx-Generated Events</h3>
<p>In addition to making it easy to <em class="test">respond</em> to events, htmx
also <em class="test">emits</em> many useful events. You can use these events to add
more functionality to your application, either via htmx itself, or by
way of scripting.</p>
<p>Here are some of the most commonly used events triggered by htmx:</p>
<dl>
<dt><code>htmx:load</code></dt>
<dd>
<p>Triggered when new content is loaded into the DOM by htmx.</p>
</dd>
<dt><code>htmx:configRequest</code></dt>
<dd>
<p>Triggered before a request is issued, allowing you to
programmatically configure the request or cancel it entirely.</p>
</dd>
<dt><code>htmx:afterRequest</code></dt>
<dd>
<p>Triggered after a request has responded.</p>
</dd>
<dt><code>htmx:abort</code></dt>
<dd>
<p>A custom event that can be sent to an htmx-powered element to abort
an open request.</p>
</dd>
</dl>
<h3 id="using-the-htmx-configrequest-event">Using the htmx:configRequest Event</h3>
<p>Let’s look at an example of how to work with htmx-emitted events.
We’ll use the <code>htmx:configRequest</code> event to configure an HTTP
request.</p>
<p>Consider the following scenario: your server-side team has decided
that they want you to include a server-generated token for extra
security on every request. The token is going to be stored in
<code>localStorage</code> in the browser, in the slot
<code>special-token</code>.</p>
<p>The token is being set via some JavaScript (don’t worry about the
details yet) when the user first logs in:</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="kw">let</span> response <span class="op">=</span> <span class="cf">await</span> <span class="fu">fetch</span>(<span class="st">"/token"</span>)<span class="op">;</span> <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a>localStorage[<span class="st">'special-token'</span>] <span class="op">=</span> <span class="cf">await</span> response<span class="op">.</span><span class="fu">text</span>()<span class="op">;</span></span></code></pre></div>
<figcaption><p>Getting The Token in JavaScript</p></figcaption>
</figure>
<ol>
<li><p>Get the value of the token then set it into localStorage</p></li>
</ol>
<p>The server-side team wants you to include this special token on every
request made by htmx, as the <code>X-SPECIAL-TOKEN</code> header. How
could you achieve this? One way would be to catch the
<code>htmx:configRequest</code> event and update the
<code>detail.headers</code> object with this token from
<code>localStorage</code>.</p>
<p>In VanillaJS, it would look something like this, placed in a
<code>&lt;script&gt;</code> tag in the <code>&lt;head&gt;</code> of our
HTML document:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="bu">document</span><span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"htmx:configRequest"</span><span class="op">,</span> configEvent <span class="kw">=&gt;</span> {</span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a> configEvent<span class="op">.</span><span class="at">detail</span><span class="op">.</span><span class="at">headers</span>[<span class="st">'X-SPECIAL-TOKEN'</span>] <span class="op">=</span> <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a> localStorage[<span class="st">'special-token'</span>]<span class="op">;</span></span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a>})</span></code></pre></div>
<figcaption><p>Adding the <code>X-SPECIAL-TOKEN</code>
header</p></figcaption>
</figure>
<ol>
<li><p>Retrieve the value from local storage and set it into a
header.</p></li>
</ol>
<p>As you can see, we add a new value to the <code>headers</code>
property of the event’s detail property. After the event handler
executes, this <code>headers</code> property is read by htmx and used to
construct the request headers for the AJAX request it makes.</p>
<p>The <code>detail</code> property of the
<code>htmx:configRequest</code> event contains a slew of useful
properties that you can update to change the “shape” of the request,
including:</p>
<dl>
<dt><code>detail.parameters</code></dt>
<dd>
<p>Allows you to add or remove request parameters</p>
</dd>
<dt><code>detail.target</code></dt>
<dd>
<p>Allows you to update the target of the request</p>
</dd>
<dt><code>detail.verb</code></dt>
<dd>
<p>Allows you to update HTTP “verb” of the request (e.g.
<code>GET</code>)</p>
</dd>
</dl>
<p>So, for example, if the server-side team decided they wanted the
token included as a parameter, rather than as a request header, you
could update your code to look like this:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="bu">document</span><span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"htmx:configRequest"</span><span class="op">,</span> configEvent <span class="kw">=&gt;</span> {</span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a> configEvent<span class="op">.</span><span class="at">detail</span><span class="op">.</span><span class="at">parameters</span>[<span class="st">'token'</span>] <span class="op">=</span> <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a> localStorage[<span class="st">'special-token'</span>]<span class="op">;</span></span>
<span id="cb8-4"><a aria-hidden="true" href="#cb8-4" tabindex="-1"></a>})</span></code></pre></div>
<figcaption><p>Adding the <code>token</code> parameter</p></figcaption>
</figure>
<ol>
<li><p>Retrieve the value from local storage and set it into a
parameter.</p></li>
</ol>
<p>As you can see, this gives you a lot of flexibility in updating the
AJAX request that htmx makes.</p>
<p>The full documentation for the <code>htmx:configRequest</code> event
(and other events you might be interested in) can be found <a href="https://htmx.org/events/#htmx:configRequest">on the htmx
website</a>.</p>
<h3 id="canceling-a-request-using-htmx-abort">Canceling a Request Using htmx:abort</h3>
<p>We can listen for any of the many useful events from htmx, and we can
respond to those events using <code>hx-trigger</code>. What else can we
do with events?</p>
<p>It turns out that htmx itself listens for one special event,
<code>htmx:abort</code>. When htmx receives this event on an element
that has a request in flight, it will abort the request.</p>
<p>Consider a situation where we have a potentially long-running request
to <code>/contacts</code>, and we want to offer a way for the users to
cancel the request. What we want is a button that issues the request,
driven by htmx, of course, and then another button that will send an
<code>htmx:abort</code> event to the first one.</p>
<p>Here is what the code might look like:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> id</span><span class="op">=</span><span class="st">"contacts-btn"</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> Get Contacts</span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb9-4"><a aria-hidden="true" href="#cb9-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span></span>
<span id="cb9-5"><a aria-hidden="true" href="#cb9-5" tabindex="-1"></a><span class="ot"> onclick</span><span class="op">=</span><span class="st">"</span></span>
<span id="cb9-6"><a aria-hidden="true" href="#cb9-6" tabindex="-1"></a><span class="st"> document.getElementById('contacts-btn')</span></span>
<span id="cb9-7"><a aria-hidden="true" href="#cb9-7" tabindex="-1"></a><span class="st"> .dispatchEvent(new Event('htmx:abort')) </span><span class="er">&lt;</span><span class="st">2&gt;</span></span>
<span id="cb9-8"><a aria-hidden="true" href="#cb9-8" tabindex="-1"></a><span class="st"> "</span><span class="dt">&gt;</span></span>
<span id="cb9-9"><a aria-hidden="true" href="#cb9-9" tabindex="-1"></a> Cancel</span>
<span id="cb9-10"><a aria-hidden="true" href="#cb9-10" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A button with an abort</p></figcaption>
</figure>
<ol>
<li><p>A normal htmx-driven <code>GET</code> request to
<code>/contacts</code></p></li>
<li><p>JavaScript to look up the button and send it an
<code>htmx:abort</code> event</p></li>
</ol>
<p>So now, if a user clicks on the “Get Contacts” button and the request
takes a while, they can click on the “Cancel” button and end the
request. Of course, in a more sophisticated user interface, you may want
to disable the “Cancel” button unless an HTTP request is in flight, but
that would be a pain to implement in pure JavaScript.</p>
<p>Thankfully this isn’t too bad to implement in hyperscript, so let’s
take a look at what that would look like:</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> id</span><span class="op">=</span><span class="st">"contacts-btn"</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="dt">&gt;</span></span>
<span id="cb10-2"><a aria-hidden="true" href="#cb10-2" tabindex="-1"></a> Get Contacts</span>
<span id="cb10-3"><a aria-hidden="true" href="#cb10-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb10-4"><a aria-hidden="true" href="#cb10-4" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span></span>
<span id="cb10-5"><a aria-hidden="true" href="#cb10-5" tabindex="-1"></a><span class="ot"> _</span><span class="op">=</span><span class="st">"on click send htmx:abort to #contacts-btn</span></span>
<span id="cb10-6"><a aria-hidden="true" href="#cb10-6" tabindex="-1"></a><span class="st"> on htmx:beforeRequest from #contacts-btn remove @disabled from me</span></span>
<span id="cb10-7"><a aria-hidden="true" href="#cb10-7" tabindex="-1"></a><span class="st"> on htmx:afterRequest from #contacts-btn add @disabled to me"</span><span class="dt">&gt;</span></span>
<span id="cb10-8"><a aria-hidden="true" href="#cb10-8" tabindex="-1"></a> Cancel</span>
<span id="cb10-9"><a aria-hidden="true" href="#cb10-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A hyperscript-Powered Button With An
Abort</p></figcaption>
</figure>
<p>Now we have a “Cancel” button that is enabled only when a request
from the <code>contacts-btn</code> button is in flight. And we are
taking advantage of htmx-generated and handled events, as well as the
event-friendly syntax of hyperscript, to make it happen. Slick!</p>
<h3 id="_server_generated_events">Server Generated Events</h3>
<p>We are going to talk more about the various ways that htmx enhances
regular HTTP requests and responses in the next section, but, since it
involves events, we are going to discuss one HTTP Response header that
htmx supports: <code>HX-Trigger</code>. We have discussed before how
HTTP requests and responses support <em class="test">headers</em>, name-value pairs
that contain metadata about a given request or response. We took
advantage of the <code>HX-Trigger</code> request header, which includes
the id of the element that triggered a given request.</p>
<p>In addition to this <em class="test">request header</em>, htmx also supports a
<em class="test">response header</em> also named <code>HX-Trigger</code>. This
response header allows you to <em class="test">trigger an event</em> on the element
that submitted an AJAX request. This turns out to be a powerful way to
coordinate elements in the DOM in a decoupled manner.</p>
<p>To see how this might work, let’s consider the following situation:
we have a button that grabs new contacts from some remote system on the
server. We will ignore the details of the server-side implementation,
but we know that if we issue a <code>POST</code> to the
<code>/sync</code> path, it will trigger a synchronization with the
system.</p>
<p>Now, this synchronization may or may not result in new contacts being
created. In the case where new contacts <em class="test">are</em> created, we want to
refresh our contacts table. In the case where no contacts are created,
we don’t want to refresh the table.</p>
<p>To implement this we could conditionally add an
<code>HX-Trigger</code> response header with the value
<code>contacts-updated</code>:</p>
<figure>
<div class="sourceCode" id="cb11"><pre class="sourceCode py"><code class="sourceCode python"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">'/sync'</span>, methods<span class="op">=</span>[<span class="st">"POST"</span>])</span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a><span class="kw">def</span> sync_with_server():</span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> contacts_updated <span class="op">=</span> RemoteServer.sync() <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a> resp <span class="op">=</span> make_response(render_template(<span class="st">'sync.html'</span>))</span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a> <span class="cf">if</span> contacts_updated <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb11-6"><a aria-hidden="true" href="#cb11-6" tabindex="-1"></a> resp.headers[<span class="st">'HX-Trigger'</span>] <span class="op">=</span> <span class="st">'contacts-updated'</span></span>
<span id="cb11-7"><a aria-hidden="true" href="#cb11-7" tabindex="-1"></a> <span class="cf">return</span> resp</span></code></pre></div>
<figcaption><p>Conditionally Triggering a <code>contacts-updated</code>
event</p></figcaption>
</figure>
<ol>
<li><p>A call to the remote system that synchronized our contact
database with it</p></li>
<li><p>If any contacts were updated we conditionally trigger the
<code>contacts-updated</code> event on the client</p></li>
</ol>
<p>This value would trigger the <code>contacts-updated</code> event on
the button that made the AJAX request to <code>/sync</code>. We can then
take advantage of the <code>from:</code> modifier of the
<code>hx-trigger</code> attribute to listen for that event. With this
pattern we can effectively trigger htmx requests from the server
side.</p>
<p>Here is what the client-side code might look like:</p>
<figure>
<div class="sourceCode" id="cb12"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb12-1"><a aria-hidden="true" href="#cb12-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/integrations/1"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb12-2"><a aria-hidden="true" href="#cb12-2" tabindex="-1"></a> Pull Contacts From Integration</span>
<span id="cb12-3"><a aria-hidden="true" href="#cb12-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb12-4"><a aria-hidden="true" href="#cb12-4" tabindex="-1"></a></span>
<span id="cb12-5"><a aria-hidden="true" href="#cb12-5" tabindex="-1"></a> ...</span>
<span id="cb12-6"><a aria-hidden="true" href="#cb12-6" tabindex="-1"></a></span>
<span id="cb12-7"><a aria-hidden="true" href="#cb12-7" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">table</span><span class="ot"> hx-get</span><span class="op">=</span><span class="st">"/contacts/table"</span></span>
<span id="cb12-8"><a aria-hidden="true" href="#cb12-8" tabindex="-1"></a><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"contacts-updated from:body"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb12-9"><a aria-hidden="true" href="#cb12-9" tabindex="-1"></a> ...</span>
<span id="cb12-10"><a aria-hidden="true" href="#cb12-10" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The Contacts Table</p></figcaption>
</figure>
<ol>
<li><p>The response to this request may conditionally trigger the
<code>contacts-updated</code> event</p></li>
<li><p>This table listens for the event and refreshes when it
occurs</p></li>
</ol>
<p>The table listens for the <code>contacts-updated</code> event, and it
does so on the <code>body</code> element. It listens on the
<code>body</code> element since the event will bubble up from the
button, and this allows us to not couple the button and table together:
we can move the button and table around as we like and, via events, the
behavior we want will continue to work fine. Additionally, we may want
<em class="test">other</em> elements or requests to trigger the
<code>contacts-updated</code> event, so this provides a general
mechanism for refreshing the contacts table in our application.</p>
<h2 id="_http_requests_responses">HTTP Requests &amp; Responses</h2>
<p>We have just seen an advanced feature of HTTP responses supported by
htmx, the <code>HX-Trigger</code> response header, but htmx supports
quite a few more headers for both requests and responses. In Chapter 4
we discussed the headers present in HTTP Requests. Here are some of the
more important headers you can use to change htmx behavior with HTTP
responses:</p>
<dl>
<dt><code>HX-Location</code></dt>
<dd>
<p>Causes a client-side redirection to a new location</p>
</dd>
<dt><code>HX-Push-Url</code></dt>
<dd>
<p>Pushes a new URL into the location bar</p>
</dd>
<dt><code>HX-Refresh</code></dt>
<dd>
<p>Refreshes the current page</p>
</dd>
<dt><code>HX-Retarget</code></dt>
<dd>
<p>Allows you to specify a new target to swap the response content into
on the client side</p>
</dd>
</dl>
<p>You can find a reference for all requests and response headers in the
<a href="https://htmx.org/reference/#headers">htmx
documentation</a>.</p>
<h3 id="http-response-codes">HTTP Response Codes</h3>
<p>Even more important than response headers, in terms of information
conveyed to the client, is the <em class="test">HTTP Response Code</em>. We discussed
HTTP Response Codes in Chapter 3. By and large htmx handles various
response codes in the manner that you would expect: it swaps content for
all 200-level response codes and does nothing for others. There are,
however, two “special” 200-level response codes:</p>
<ul>
<li><p><code>204 No Content</code> - When htmx receives this response
code, it will <em class="test">not</em> swap any content into the DOM (even if the
response has a body)</p></li>
<li><p><code>286</code> - When htmx receives this response code to a
request that is polling, it will stop the polling</p></li>
</ul>
<p>You can override the behavior of htmx with respect to response codes
by, you guessed it, responding to an event! The
<code>htmx:beforeSwap</code> event allows you to change the behavior of
htmx with respect to various status codes.</p>
<p>Let’s say that, rather than doing nothing when a <code>404</code>
occurred, you wanted to alert the user that an error had occurred. To do
so, you want to invoke a JavaScript method,
<code>showNotFoundError()</code>. Let’s add some code to use the
<code>htmx:beforeSwap</code> event to make this happen:</p>
<figure>
<div class="sourceCode" id="cb13"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb13-1"><a aria-hidden="true" href="#cb13-1" tabindex="-1"></a><span class="bu">document</span><span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">'htmx:beforeSwap'</span><span class="op">,</span> evt <span class="kw">=&gt;</span> { <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb13-2"><a aria-hidden="true" href="#cb13-2" tabindex="-1"></a> <span class="cf">if</span> (evt<span class="op">.</span><span class="at">detail</span><span class="op">.</span><span class="at">xhr</span><span class="op">.</span><span class="at">status</span> <span class="op">===</span> <span class="dv">404</span>) { <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb13-3"><a aria-hidden="true" href="#cb13-3" tabindex="-1"></a> <span class="fu">showNotFoundError</span>()<span class="op">;</span></span>
<span id="cb13-4"><a aria-hidden="true" href="#cb13-4" tabindex="-1"></a> }</span>
<span id="cb13-5"><a aria-hidden="true" href="#cb13-5" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div>
<figcaption><p>Showing a 404 dialog</p></figcaption>
</figure>
<ol>
<li><p>Hook into the <code>htmx:beforeSwap</code> event.</p></li>
<li><p>If the response code is a <code>404</code>, show the user a
dialog.</p></li>
</ol>
<p>You can also use the <code>htmx:beforeSwap</code> event to configure
if the response should be swapped into the DOM and what element the
response should target. This gives you quite a bit of flexibility in
choosing how you want to use HTTP Response codes in your application.
Full documentation on the <code>htmx:beforeSwap</code> event can be
found at <a href="https://htmx.org/events/#htmx:beforeSwap">htmx.org</a>.</p>
<h2 id="_updating_other_content">Updating Other Content</h2>
<p>Above we saw how to use a server-triggered event, via the
<code>HX-Trigger</code> HTTP response header, to update a piece of the
DOM based on the response to another part of the DOM. This technique
addresses the general problem that comes up in Hypermedia-Driven
Applications: “How do I update other content?” After all, in normal HTTP
requests, there is only one “target”, the entire screen, and, similarly,
in htmx-based requests, there is only one target: either the explicit or
implicit target of the element.</p>
<p>If you want to update other content in htmx, you have a few
options:</p>
<h3 id="_expanding_your_selection">Expanding Your Selection</h3>
<p>The first option, and the simplest, is to “expand the target.” That
is, rather than simply replacing a small part of the screen, expand the
target of your htmx-driven request until it is large enough to enclose
all the elements that need to be updated on a screen. This has the
tremendous advantage of being simple and reliable. The downside is that
it may not provide the user experience that you want, and it may not
play well with a particular server-side template layout. Regardless, we
always recommend at least thinking about this approach first.</p>
<h3 id="out-of-band-swaps">Out of Band Swaps</h3>
<p>A second option, a bit more complex, is to take advantage of “Out Of
Band” content support in htmx. When htmx receives a response, it will
inspect it for top-level content that includes the
<code>hx-swap-oob</code> attribute. That content will be removed from
the response, so it will not be swapped into the DOM in the normal
manner. Instead, it will be swapped in for the content that it matches
by id.</p>
<p>Let’s look at an example. Consider the situation we had earlier,
where a contacts table needs to be updated if an integration pulls down
any new contacts. Previously we solved this by using events and a
server-triggered event via the <code>HX-Trigger</code> response
header.</p>
<p>This time, we’ll use the <code>hx-swap-oob</code> attribute in the
response to the <code>POST</code> to <code>/integrations/1</code>. The
new contacts table content will “piggyback” on the response.</p>
<figure>
<div class="sourceCode" id="cb14"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb14-1"><a aria-hidden="true" href="#cb14-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> hx-post</span><span class="op">=</span><span class="st">"/integrations/1"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb14-2"><a aria-hidden="true" href="#cb14-2" tabindex="-1"></a> Pull Contacts From Integration</span>
<span id="cb14-3"><a aria-hidden="true" href="#cb14-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb14-4"><a aria-hidden="true" href="#cb14-4" tabindex="-1"></a></span>
<span id="cb14-5"><a aria-hidden="true" href="#cb14-5" tabindex="-1"></a> ...</span>
<span id="cb14-6"><a aria-hidden="true" href="#cb14-6" tabindex="-1"></a></span>
<span id="cb14-7"><a aria-hidden="true" href="#cb14-7" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">table</span><span class="ot"> id</span><span class="op">=</span><span class="st">"contacts-table"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb14-8"><a aria-hidden="true" href="#cb14-8" tabindex="-1"></a> ...</span>
<span id="cb14-9"><a aria-hidden="true" href="#cb14-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>The updated contacts table</p></figcaption>
</figure>
<ol>
<li><p>The button still issues a <code>POST</code> to
<code>/integrations/1</code>.</p></li>
<li><p>The table no longer listens for an event, but it now has an
id.</p></li>
</ol>
<p>Next, the response to the <code>POST</code> to
<code>/integrations/1</code> will include the content that needs to be
swapped into the button, per the usual htmx mechanism. But it will also
include a new, updated version of the contacts table, which will be
marked as <code>hx-swap-oob="true"</code>. This content will be removed
from the response so that it is not inserted into the button. Instead,
it is swapped into the DOM in place of the existing table since it has a
matching id.</p>
<figure>
<pre><code>HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
...
Pull Contacts From Integration &lt;1&gt;
&lt;table id="contacts-table" hx-swap-oob="true"&gt; &lt;2&gt;
...
&lt;/table&gt;
</code></pre>
<figcaption><p>A response with out-of-band content</p></figcaption>
</figure>
<ol>
<li><p>This content will be placed in the button.</p></li>
<li><p>This content will be removed from the response and swapped by
id.</p></li>
</ol>
<p>Using this piggybacking technique, you can update content wherever
needed on a page. The <code>hx-swap-oob</code> attribute supports other
additional features, all of which are <a href="https://htmx.org/attributes/hx-swap-oob/">documented</a>.</p>
<p>Depending on how exactly your server-side templating technology
works, and what level of interactivity your application requires, out of
band swapping can be a powerful mechanism for content updates.</p>
<h3 id="events-1">Events</h3>
<p>Finally, the most complex mechanism for updating content is the one
we saw back in the events section: using server-triggered events to
update elements. This approach can be very clean, but also requires a
deeper conceptual knowledge of HTML and events, and a commitment to the
event-driven approach. While we like this style of development, it isn’t
for everyone. We typically recommend this pattern only if the htmx
philosophy of event-driven hypermedia really speaks to you.</p>
<p>If it <em class="test">does</em> speak to you, however, we say: go for it. We’ve
created some very complex and flexible user interfaces using this
approach, and we are quite fond of it.</p>
<h3 id="being-pragmatic">Being Pragmatic</h3>
<p>All of these approaches to the “Updating Other Content” problem will
work, and will often work well. However, there may come a point where it
would just be simpler to use a different approach for your UI, like the
reactive one. As much as we like the hypermedia approach, the reality is
that there are some UX patterns that simply cannot be implemented easily
using it. The canonical example of this sort of pattern, which we have
mentioned before, is something like a live online spreadsheet: it is
simply too complex a user interface, with too many interdependencies, to
be done well via exchanges of hypermedia with a server.</p>
<p>In cases like this, and any time you feel like an htmx-based solution
is proving to be more complex than another approach might be, we
recommend that you consider a different technology. Be pragmatic, and
use the right tool for the job. You can always use htmx for the parts of
your application that aren’t as complex and don’t need the full
complexity of a reactive framework, and save that complexity budget for
the parts that do.</p>
<p>We encourage you to learn many different web technologies, with an
eye to the strengths and weaknesses of each one. This will give you a
deep tool chest to reach into when problems present themselves. Our
experience is that, with htmx, hypermedia is a tool you can reach for
frequently.</p>
<h2 id="debugging">Debugging</h2>
<p>We are not ashamed to admit: we are big fans of events. They are the
underlying technology of almost any interesting user interface, and are
particularly useful in the DOM once they have been unlocked for general
use in HTML. They let you build nicely decoupled software while often
preserving the locality of behavior we like so much.</p>
<p>However, events are not perfect. One area where events can be
particularly tricky to deal with is <em class="test">debugging</em>: you often want
to know why an event <em class="test">isn’t</em> happening. But where can you set a
break point for something that <em class="test">isn’t</em> happening? The answer, as
of right now, is: you can’t.</p>
<p>There are two techniques that can help in this regard, one provided
by htmx, the other provided by Chrome, the browser by Google.</p>
<h3 id="_logging_htmx_events">Logging Htmx Events</h3>
<p>The first technique, provided by htmx itself, is to call the
<code>htmx.logAll()</code> method. When you do this, htmx will log all
the internal events that occur as it goes about its business, loading up
content, responding to events and so forth.</p>
<p>This can be overwhelming, but with judicious filtering can help you
zero in on a problem. Here are what (a bit of) the logs look like when
clicking on the “docs” link on <a href="https://htmx.org">https://htmx.org</a>, with <code>logAll()</code>
enabled:</p>
<figure>
<pre class="text"><code>htmx:configRequest
&lt;a href="/docs/"&gt;
Object { parameters: {}, unfilteredParameters: {}, headers: {…}, target: body, verb: "get", errors: [], withCredentials: false, timeout: 0, path: "/docs/", triggeringEvent: a
, … }
htmx.js:439:29
htmx:beforeRequest
&lt;a href="/docs/"&gt;
Object { xhr: XMLHttpRequest, target: body, requestConfig: {…}, etc: {}, pathInfo: {…}, elt: a
}
htmx.js:439:29
htmx:beforeSend
&lt;a class="htmx-request" href="/docs/"&gt;
Object { xhr: XMLHttpRequest, target: body, requestConfig: {…}, etc: {}, pathInfo: {…}, elt: a.htmx-request
}
htmx.js:439:29
htmx:xhr:loadstart
&lt;a class="htmx-request" href="/docs/"&gt;
Object { lengthComputable: false, loaded: 0, total: 0, elt: a.htmx-request
}
htmx.js:439:29
htmx:xhr:progress
&lt;a class="htmx-request" href="/docs/"&gt;
Object { lengthComputable: true, loaded: 4096, total: 19915, elt: a.htmx-request
}
htmx.js:439:29
htmx:xhr:progress
&lt;a class="htmx-request" href="/docs/"&gt;
Object { lengthComputable: true, loaded: 19915, total: 19915, elt: a.htmx-request
}
htmx.js:439:29
htmx:beforeOnLoad
&lt;a class="htmx-request" href="/docs/"&gt;
Object { xhr: XMLHttpRequest, target: body, requestConfig: {…}, etc: {}, pathInfo: {…}, elt: a.htmx-request
}
htmx.js:439:29
htmx:beforeSwap
&lt;body hx-ext="class-tools, preload"&gt;
</code></pre>
<figcaption><p>Htmx logs</p></figcaption>
</figure>
<p>Not exactly easy on the eyes, is it?</p>
<p>But, if you take a deep breath and squint, you can see that it isn’t
<em class="test">that</em> bad: a series of htmx events, some of which we have seen
before (there’s <code>htmx:configRequest</code>!), get logged to the
console, along with the element they are triggered on.</p>
<p>After a bit of reading and filtering, you will be able to make sense
of the event stream, and it can help you debug htmx-related issues.</p>
<h3 id="_monitoring_events_in_chrome">Monitoring Events in Chrome</h3>
<p>The preceding technique is useful if the problem is occurring
somewhere <em class="test">within</em> htmx, but what if htmx is never getting
triggered at all? This comes up some times, like when, for example, you
have accidentally typed an event name incorrectly somewhere.</p>
<p>In cases like this you will need recourse to a tool available in the
browser itself. Fortunately, the Chrome browser by Google provides a
very useful function, <code>monitorEvents()</code>, that allows you to
monitor <em class="test">all</em> events that are triggered on an element.</p>
<p>This feature is available <em class="test">only</em> in the console, so you can’t
use it in code on your page. But, if you are working with htmx in
Chrome, and are curious why an event isn’t triggering on an element, you
can open the developers console and type the following:</p>
<figure>
<div class="sourceCode" id="cb17"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb17-1"><a aria-hidden="true" href="#cb17-1" tabindex="-1"></a><span class="fu">monitorEvents</span>(<span class="bu">document</span><span class="op">.</span><span class="fu">getElementById</span>(<span class="st">"some-element"</span>))<span class="op">;</span></span></code></pre></div>
<figcaption><p>Htmx logs</p></figcaption>
</figure>
<p>This will then print <em class="test">all</em> the events that are triggered on
the element with the id <code>some-element</code> to the console. This
can be very useful for understanding exactly which events you want to
respond to with htmx, or troubleshooting why an expected event isn’t
occurring.</p>
<p>Using these two techniques will help you as you (infrequently, we
hope) troubleshoot event-related issues when developing with htmx.</p>
<h2 id="security-considerations">Security Considerations</h2>
<p>In general, htmx and hypermedia tends to be more secure than
JavaScript heavy approaches to building web applications. This is
because, by moving much of the processing to the back end, the
hypermedia approach tends not to expose as much surface area of your
system to end users for manipulation and shenanigans.</p>
<p>However, even with hypermedia, there are still situations that
require care when doing development. Of particular concern are
situations where user-generated content is shown to other users: a
clever user might try to insert htmx code that tricks the other users
into clicking on content that triggers actions they don’t want to
take.</p>
<p>In general, all user-generated content should be escaped on the
server-side, and most server-side rendering frameworks provide
functionality for handling this situation. But there is always a risk
that something slips through the cracks.</p>
<p>In order to help you sleep better at night, htmx provides the
<code>hx-disable</code> attribute. When this attribute is placed on an
element, all htmx attributes within that element will be ignored.</p>
<h3 id="content-security-policies---htmx">Content Security Policies &amp; Htmx</h3>
<p>A Content Security Policy (CSP) is a browser technology that allows
you to detect and prevent certain types of content injection-based
attacks. A full discussion of CSPs is beyond the scope of this book, but
we refer you to the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">Mozilla
Developer Network article</a> on the topic for more information.</p>
<p>A common feature to disable using a CSP is the <code>eval()</code>
feature of JavaScript, which allows you to evaluate arbitrary JavaScript
code from a string. This has proven to be a security issue and many
teams have decided that it is not worth the risk to keep it enabled in
their web applications.</p>
<p>Htmx does not make heavy use of <code>eval()</code> and, thus, a CSP
with this restriction in place will be fine. The one feature that does
rely on <code>eval()</code> is event filters, discussed above. If you
decide to disable <code>eval()</code> for your web application, you will
not be able to use the event filtering syntax.</p>
<h2 id="configuring">Configuring</h2>
<p>There are a large number of configuration options available for htmx.
Some examples of things you can configure are:</p>
<ul>
<li><p>The default swap style</p></li>
<li><p>The default swap delay</p></li>
<li><p>The default timeout of AJAX requests</p></li>
</ul>
<p>A full list of configuration options can be found in the config
section of the <a href="https://htmx.org/docs/#config">main htmx
documentation</a>.</p>
<p>Htmx is typically configured via a <code>meta</code> tag, found in
the header of a page. The name of the meta tag should be
<code>htmx-config</code>, and the content attribute should contain the
configuration overrides, formatted as JSON. Here is an example:</p>
<figure>
<div class="sourceCode" id="cb18"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb18-1"><a aria-hidden="true" href="#cb18-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">meta</span><span class="ot"> name</span><span class="op">=</span><span class="st">"htmx-config"</span><span class="ot"> content</span><span class="op">=</span><span class="st">'{"defaultSwapStyle":"outerHTML"}'</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>An htmx configuration via <code>meta</code>
tag</p></figcaption>
</figure>
<p>In this case, we are overriding the default swap style from the usual
<code>innerHTML</code> to <code>outerHTML</code>. This might be useful
if you find yourself using <code>outerHTML</code> more frequently than
<code>innerHTML</code> and want to avoid having to explicitly set that
swap value throughout your application.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Semantic HTML</h2>
<p>Telling people to “use semantic HTML” instead of “read the spec” has
led to a lot of people guessing at the meaning of tags — “looks pretty
semantic to me!” — instead of engaging with the spec.</p>
<blockquote>
<p>I think being asked to write <em class="test">meaningful</em> HTML better lights
the path to realizing that it isn’t about what the text means to
humans—​it’s about using tags for the purpose outlined in the specs to
meet the needs of software like browsers, assistive technologies, and
search engines.</p>
</blockquote><p class="quote-attribution"> <a href="https://t-ravis.com/post/doc/semantic_the_8_letter_s-word/">https://t-ravis.com/post/doc/semantic_the_8_letter_s-word/</a></p>
<p>We recommend talking about, and writing, <em class="test">conformant</em> HTML.
(We can always bikeshed further). Use the elements to the full extent
provided by the HTML specification, and let the software take from it
whatever meaning they can.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Client Side Scripting</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#_is_scripting_allowed">Is Scripting Allowed?</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#scripting-for-hypermedia">Scripting for Hypermedia</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_scripting_tools_for_the_web">Scripting Tools for the Web</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#vanilla-javascript">Vanilla JavaScript</a>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#_a_simple_counter">A Simple Counter</a>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#_an_inline_implementation">An inline implementation</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_separating_our_scripting_out">Separating our scripting out</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#locality-of-behavior">Locality of Behavior</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#what-to-do-with-our-counter-">What to do with our counter?</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#rsjs">RSJS</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_vanillajs_in_action_an_overflow_menu">VanillaJS in Action: An
Overflow Menu</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#alpine-js">Alpine.js</a>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#-x-on-click--vs---onclick-">“x-on:click” vs. “onclick”</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#reactivity-and-templating">Reactivity and Templating</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_alpine_js_in_action_a_bulk_action_toolbar">Alpine.js in Action:
A Bulk Action Toolbar</a>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#_implementing_actions">Implementing actions</a>
</li></ul>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#-hyperscript">_hyperscript</a>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#_hyperscript_in_action_a_keyboard_shortcut">_hyperscript in
Action: A Keyboard Shortcut</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_why_a_new_programming_language">Why a New Programming
Language?</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_using_off_the_shelf_components">Using Off-the-Shelf
Components</a>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#_integration_options">Integration Options</a>
<ul>
<li>
<a href="https://hypermedia.systems/client-side-scripting/#_integrating_using_callbacks">Integrating using callbacks</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_integrating_using_events">Integrating using events</a>
</li></ul>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#_pragmatic_scripting">Pragmatic Scripting</a>
</li><li>
<a href="https://hypermedia.systems/client-side-scripting/#html-note-title">HTML Notes: HTML is for Applications</a>
</li></ul>
</details>
<div class="division-content">
<blockquote>
<p>REST allows client functionality to be extended by downloading and
executing code in the form of applets or scripts. This simplifies
clients by reducing the number of features required to be
pre-implemented.</p>
</blockquote><p class="quote-attribution"> Roy Fielding, Architectural Styles and the Design of Network-based
Software Architectures</p>
<p>Thus far we have (mostly) avoided writing any JavaScript (or
_hyperscript) in Contact.app, mainly because the functionality we
implemented has not required it. In this chapter we are going to look at
scripting and, in particular, hypermedia-friendly scripting within the
context of a Hypermedia-Driven Application.</p>
<h2 id="_is_scripting_allowed">Is Scripting Allowed?</h2>
<p>A common criticism of the web is that it’s being misused. There is a
narrative that WWW was created as a delivery system for “documents”, and
only came to be used for “applications” by way of an accident or bizarre
circumstances.</p>
<p>However, the concept of hypermedia challenges the split of document
and application. Hypermedia systems like HyperCard, which preceded the
web, featured rich capabilities for active and interactive experiences,
including scripting.</p>
<p>HTML, as specified and implemented, does lack affordances needed to
build highly interactive applications. This doesn’t mean, however, that
hypermedia’s <em class="test">purpose</em> is “documents” over “applications.”</p>
<p>Rather, while the theoretical foundation is there, the implementation
is underdeveloped. With JavaScript being the only extension point and
hypermedia controls not being well integrated to JavaScript (why can’t
one click a link without halting the program?), developers have not
internalized hypermedia and have instead used the web as a dumb pipe for
apps that imitate “native” ones.</p>
<p>A goal of this book is to show that it is possible to build
sophisticated web applications using the original technology of the web,
hypermedia, without the application developer needing to reach for the
abstractions provided by the large, popular JavaScript frameworks.</p>
<p>Htmx itself is, of course, written in JavaScript, and one of its
advantages is that hypermedia interactions that go through htmx expose a
rich interface to JavaScript code with configuration, events, and htmx’s
own extension support.</p>
<p>Htmx expands the expressiveness of HTML enough that it removes the
need for scripting in many situations. This makes htmx attractive to
people who don’t want to write JavaScript, and there are many of those
sorts of developers, wary of the complexity of Single Page Application
frameworks.</p>
<p>However, dunking on JavaScript is not the aim of the htmx project.
The goal of htmx is not less JavaScript, but less code, more readable
and hypermedia-friendly code.</p>
<p>Scripting has been a massive force multiplier for the web. Using
scripting, web application developers are not only able to enhance their
HTML websites, but also create full-fledged client-side applications
that can often compete with native, thick client applications.</p>
<p>This JavaScript-centric approach to building web applications is a
testament to the power of the web and to the sophistication of web
browsers in particular. It has its place in web development: there are
situations where the hypermedia approach simply can’t provide the level
of interaction that an SPA can.</p>
<p>However, in addition to this more JavaScript-centric style, we want
to develop a style of scripting more compatible and consistent with
Hypermedia-Driven Applications.</p>
<h2 id="scripting-for-hypermedia">Scripting for Hypermedia</h2>
<p>Borrowing from Roy Fielding’s notion of “constraints” defining REST,
we offer two constraints of hypermedia-friendly scripting. You are
scripting in an HDA-compatible manner if the following two constraints
are adhered to:</p>
<ul>
<li><p>The main data format exchanged between server and client must be
hypermedia, the same as it would be without scripting.</p></li>
<li><p>Client-side state, outside the DOM itself, is kept to a
minimum.</p></li>
</ul>
<p>The goal of these constraints is to confine scripting to where it
shines best and where nothing else comes close: <em class="test">interaction
design</em>. Business logic and presentation logic are the
responsibility of the server, where we can pick whichever languages or
tools are appropriate for our business domain.</p>
<div>
<div id="sidebar">
<div>
<div>
<p><strong>The Server</strong></p>
</div>
<div>
<p>Keeping business logic and presentation logic both “on the server”
does not mean these two “concerns” are mixed or coupled. They can be
modularized on the server. In fact, they <em class="test">should</em> be modularized
on the server, along with all the other concerns of our application.</p>
<p>Note also that, especially in web development parlance, the humble
“server” is usually a whole fleet of racks, virtual machines, containers
and more. Even a worldwide network of datacenters is reduced to “the
server” when discussing the server-side of a Hypermedia-Driven
Application.</p>
</div>
</div>
</div>
</div>
<p>Satisfying these two constraints sometimes requires us to diverge
from what is typically considered best practice for JavaScript. Keep in
mind that the cultural wisdom of JavaScript was largely developed in
JavaScript-centric SPA applications.</p>
<p>The Hypermedia-Driven Application cannot as comfortably fall back on
this tradition. This chapter is our contribution to the development of a
new style and best practices for what we are calling Hypermedia-Driven
Applications.</p>
<p>Unfortunately, simply listing “best practices” is rarely convincing
or edifying. To be honest, it’s boring.</p>
<p>Instead, we will demonstrate these best practices by implementing
client-side features in Contact.app. To cover different aspects of
hypermedia-friendly scripting, we will implement three different
features:</p>
<ul>
<li><p>An overflow menu to hold the <em class="test">Edit</em>, <em class="test">View</em> and
<em class="test">Delete</em> actions, to clean up visual clutter in our list of
contacts.</p></li>
<li><p>An improved interface for bulk deletion.</p></li>
<li><p>A keyboard shortcut for focusing the search box.</p></li>
</ul>
<p>The important takeaway in the implementation of each of these
features is that, while they are implemented entirely on the client-side
using scripting, they <em class="test">don’t exchange information with the
server</em> via a non-hypermedia format, such as JSON, and that they
don’t store a significant amount of state outside of the DOM itself.</p>
<h2 id="_scripting_tools_for_the_web">Scripting Tools for the Web</h2>
<p>The primary scripting language for the web is, of course, JavaScript,
which is ubiquitous in web development today.</p>
<p>A bit of interesting internet lore, however, is that JavaScript was
not always the only built-in option. As the quote from Roy Fielding at
the start of this chapter hints, “applets” written in other languages
such as Java were considered to be part of the scripting infrastructure
of the web. In addition, there was a time period when Internet Explorer
supported VBScript, a scripting language based on Visual Basic.</p>
<p>Today, we have a variety of <em class="test">transcompilers</em> (often shortened
to <em class="test">transpilers</em>) that convert many languages to JavaScript, such
as TypeScript, Dart, Kotlin, ClojureScript, F# and more. There is also
the WebAssembly (WASM) bytecode format, which is supported as a
compilation target for C, Rust, and the WASM-first language
AssemblyScript.</p>
<p>However, most of these options are not geared towards a
hypermedia-friendly style of scripting. Compile-to-JS languages are
often paired with SPA-oriented libraries (Dart and AngularDart,
ClojureScript and Reagent, F# and Elm), and WASM is currently mainly
geared toward linking to C/C++ libraries from JavaScript.</p>
<p>We will instead focus on three client-side scripting technologies
that <em class="test">are</em> hypermedia-friendly:</p>
<ul>
<li><p>VanillaJS, that is, using JavaScript without depending on any
framework.</p></li>
<li><p>Alpine.js, a JavaScript library for adding behavior directly in
HTML.</p></li>
<li><p>_hyperscript, a non-JavaScript scripting language created
alongside htmx. Like AlpineJS, _hyperscript is usually embedded in
HTML.</p></li>
</ul>
<p>Let’s take a quick look at each of these scripting options, so we
know what we are dealing with.</p>
<p>Note that, as with CSS, we are going to show you just enough of each
of these options to give a flavor of how they work and, we hope, spark
your interest in looking into any of them more extensively.</p>
<h2 id="vanilla-javascript">Vanilla JavaScript</h2>
<blockquote>
<p>No code is faster than no code.</p>
</blockquote><p class="quote-attribution"> Merb (Ruby web framework), motto</p>
<p>Vanilla JavaScript is simply using plain JavaScript in your
application, without any intermediate layers. The term “Vanilla” entered
frontend web dev parlance as it became assumed that any sufficiently
“advanced” web app would use some library with a name ending in “.js”.
As JavaScript matured as a scripting language, however, standardized
across browsers and provided more and more functionality, these
frameworks and libraries became less important.</p>
<p>Somewhat ironically though, as JavaScript became more powerful and
removed the need for the first generation of JavaScript libraries such
as jQuery, it also enabled people to build complex SPA libraries. These
SPA libraries are often even more elaborate than the original first
generation of JavaScript libraries.</p>
<p>A quote from the website <a href="http://vanilla-js.com">http://vanilla-js.com</a>, which is well
worth visiting even though it’s slightly out of date, captures the
situation well:</p>
<blockquote>
<p>VanillaJS is the lowest-overhead, most comprehensive framework I’ve
ever used.</p>
</blockquote><p class="quote-attribution"> http://vanilla-js.com</p>
<p>With JavaScript having matured as a scripting language, this is
certainly the case for many applications. It is especially true in the
case of HDAs, since, by using hypermedia, your application will not need
many of the features typically provided by more elaborate Single Page
Application JavaScript frameworks:</p>
<ul>
<li><p>Client-side routing</p></li>
<li><p>An abstraction over DOM manipulation (i.e., templates that
automatically update when referenced variables change)</p></li>
<li><p>Server side rendering <a class="footnote-ref" href="#fn1" id="fnref1" role="doc-noteref"><sup>1</sup></a></p></li>
<li><p>Attaching dynamic behavior to server-rendered tags on load (i.e.,
“hydration”)</p></li>
<li><p>Network requests</p></li>
</ul>
<p>Without all this complexity being handled in JavaScript, your
framework needs are dramatically reduced.</p>
<p>One of the best things about VanillaJS is how you install it: you
don’t have to!</p>
<p>You can just start writing JavaScript in your web application, and it
will simply work.</p>
<p>That’s the good news. The bad news is that, despite improvements over
the last decade, JavaScript has some significant limitations as a
scripting language that can make it less than ideal as a stand-alone
scripting technology for Hypermedia-Driven Applications:</p>
<ul>
<li><p>Being as established as it is, it has accreted a lot of features
and warts.</p></li>
<li><p>It has a complicated and confusing set of features for working
with asynchronous code.</p></li>
<li><p>Working with events is surprisingly difficult.</p></li>
<li><p>DOM APIs (a large portion of which were originally designed for
Java, yes <em class="test">Java</em>) are verbose and don’t have a habit of making
common functionality easy to use.</p></li>
</ul>
<p>None of these limitations are deal-breakers, of course. Many of them
are gradually being fixed and many people prefer the “close to the
metal” (for lack of a better term) nature of vanilla JavaScript over
more elaborate client-side scripting approaches.</p>
<h3 id="_a_simple_counter">A Simple Counter</h3>
<p>To dive into vanilla JavaScript as a front end scripting option,
let’s create a simple counter widget.</p>
<p>Counter widgets are a common “Hello World” example for JavaScript
frameworks, so looking at how it can be done in vanilla JavaScript (as
well as the other options we are going to look at) will be
instructive.</p>
<p>Our counter widget will be very simple: it will have a number, shown
as text, and a button that increments the number.</p>
<p>One problem with tackling this problem in vanilla JavaScript is that
it lacks one thing that most JavaScript frameworks provide: a default
code and architectural style.</p>
<p>With vanilla JavaScript, there are no rules!</p>
<p>This isn’t all bad. It presents a great opportunity to take a small
journey through various styles that people have developed for writing
their JavaScript.</p>
<h4 id="_an_inline_implementation">An inline implementation</h4>
<p>To begin, let’s start with the simplest thing imaginable: all of our
JavaScript will be written inline, directly in the HTML. When the button
is clicked, we will look up the <code>output</code> element holding the
number, and increment the number contained within it.</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">section</span><span class="ot"> class</span><span class="op">=</span><span class="st">"counter"</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">output</span><span class="ot"> id</span><span class="op">=</span><span class="st">"my-output"</span><span class="dt">&gt;</span>0<span class="dt">&lt;/</span><span class="kw">output</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span></span>
<span id="cb1-4"><a aria-hidden="true" href="#cb1-4" tabindex="-1"></a><span class="ot"> onclick</span><span class="op">=</span><span class="st">" </span><span class="er">&lt;</span><span class="st">2&gt;</span></span>
<span id="cb1-5"><a aria-hidden="true" href="#cb1-5" tabindex="-1"></a><span class="st"> document.querySelector('#my-output') </span><span class="er">&lt;</span><span class="st">3&gt;</span></span>
<span id="cb1-6"><a aria-hidden="true" href="#cb1-6" tabindex="-1"></a><span class="st"> .textContent++ </span><span class="er">&lt;</span><span class="st">4&gt;</span></span>
<span id="cb1-7"><a aria-hidden="true" href="#cb1-7" tabindex="-1"></a><span class="st"> "</span></span>
<span id="cb1-8"><a aria-hidden="true" href="#cb1-8" tabindex="-1"></a><span class="ot"> </span><span class="dt">&gt;</span>Increment<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb1-9"><a aria-hidden="true" href="#cb1-9" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">section</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Counter in vanilla JavaScript, inline
version</p></figcaption>
</figure>
<ol>
<li><p>Our output element has an ID to help us find it.</p></li>
<li><p>We use the <code>onclick</code> attribute to add an event
listener.</p></li>
<li><p>Find the output via a querySelector() call.</p></li>
<li><p>JavaScript allows us use the <code>++</code> operator on
strings.</p></li>
</ol>
<p>Not too bad.</p>
<p>It’s not the most beautiful code, and can be irritating especially if
you aren’t used to the DOM APIs.</p>
<p>It’s a little annoying that we needed to add an <code>id</code> to
the <code>output</code> element. The
<code>document.querySelector()</code> function is a bit verbose compared
with, say, the <code>$</code> function, as provided by jQuery.</p>
<p>But it works. It’s also easy enough to understand, and crucially it
doesn’t require any other JavaScript libraries.</p>
<p>So that’s the simple, inline approach with VanillaJS.</p>
<h4 id="_separating_our_scripting_out">Separating our scripting out</h4>
<p>While the inline implementation is simple in some sense, a more
standard way to write this would be to move the code into a separate
JavaScript file. This JavaScript file would then either be linked to via
a <code>&lt;script src&gt;</code> tag or placed into an inline
<code>&lt;script&gt;</code> tag by a build process.</p>
<p>Here we see the HTML and JavaScript <em class="test">separated out</em> from one
another, in different files. The HTML is now “cleaner” in that there is
no JavaScript in it.</p>
<p>The JavaScript is a bit more complex than in our inline version: we
need to look up the button using a query selector and add an <em class="test">event
listener</em> to handle the click event and increment the counter.</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">section</span><span class="ot"> class</span><span class="op">=</span><span class="st">"counter"</span><span class="dt">&gt;</span></span>
<span id="cb2-2"><a aria-hidden="true" href="#cb2-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">output</span><span class="ot"> id</span><span class="op">=</span><span class="st">"my-output"</span><span class="dt">&gt;</span>0<span class="dt">&lt;/</span><span class="kw">output</span><span class="dt">&gt;</span></span>
<span id="cb2-3"><a aria-hidden="true" href="#cb2-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> class</span><span class="op">=</span><span class="st">"increment-btn"</span><span class="dt">&gt;</span>Increment<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb2-4"><a aria-hidden="true" href="#cb2-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">section</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Counter HTML</p></figcaption>
</figure>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="kw">const</span> counterOutput <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">querySelector</span>(<span class="st">"#my-output"</span>)<span class="op">,</span> <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> incrementBtn <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">querySelector</span>(<span class="st">".counter .increment-btn"</span>) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a>incrementBtn<span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"click"</span><span class="op">,</span> e <span class="kw">=&gt;</span> { <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb3-5"><a aria-hidden="true" href="#cb3-5" tabindex="-1"></a> counterOutput<span class="op">.</span><span class="at">innerHTML</span><span class="op">++</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span>
<span id="cb3-6"><a aria-hidden="true" href="#cb3-6" tabindex="-1"></a>})</span></code></pre></div>
<figcaption><p>Counter JavaScript</p></figcaption>
</figure>
<ol>
<li><p>Find the output element.</p></li>
<li><p>Find the button.</p></li>
<li><p>We use <code>addEventListener</code>, which is preferable to
<code>onclick</code> for many reasons.</p></li>
<li><p>The logic stays the same, only the structure around it
changes.</p></li>
</ol>
<p>In moving the JavaScript out to another file, we are following a
software design principle known as <em class="test">Separation of Concerns
(SoC).</em></p>
<p>Separation of Concerns posits that the various “concerns” (or
aspects) of a software project should be divided up into multiple files,
so that they don’t “pollute” one another. JavaScript isn’t markup, so it
shouldn’t be in your HTML, it should be <em class="test">elsewhere</em>. Styling
information, similarly, isn’t markup, and so it belongs in a separate
file as well (A CSS file, for example.)</p>
<p>For quite some time, this Separation of Concerns was considered the
“orthodox” way to build web applications.</p>
<p>A stated goal of Separation of Concerns is that we should be able to
modify and evolve each concern independently, with confidence that we
won’t break any of the other concerns.</p>
<p>However, let’s look at exactly how this principle has worked out in
our simple counter example. If you look closely at the new HTML, it
turns out that we’ve had to add a class to the button. We added this
class so that we could look the button up in JavaScript and add in an
event handler for the “click” event.</p>
<p>Now, in both the HTML and the JavaScript, this class name is just a
string and there isn’t any process to <em class="test">verify</em> that the button
has the right classes on it or its ancestors to ensure that the event
handler is actually added to the right element.</p>
<p>Unfortunately, it has turned out that the careless use of CSS
selectors in JavaScript can cause what is known as <em class="test">jQuery soup</em>.
jQuery soup is a situation where:</p>
<ul>
<li><p>The JavaScript that attaches a given behavior to a given element
is difficult to find.</p></li>
<li><p>Code reuse is difficult.</p></li>
<li><p>The code ends up wildly disorganized and “flat”, with lots of
unrelated event handlers mixed together.</p></li>
</ul>
<p>The name “jQuery soup” comes from the fact that most JavaScript-heavy
applications used to be built in jQuery (many still are), which, perhaps
inadvertently, tended to encourage this style of JavaScript.</p>
<p>So, you can see that the notion of Separation of Concerns doesn’t
always work as well as promised: our concerns end up intertwined or
coupled pretty deeply, even when we separate them into different
files.</p>
<figure>
<div>
<div data-align="start">
<pre><code> EXPECTATION REALITY
HTML CSS JS HTML CSS JS
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐
│ │ │ │ │ │ │ │ │ │ │ │
│ C │ │ C │ │ c │ │ CO │ │ NC │ │ern │
│ O │ │ o │ │ o │ │ │ │ │ │ │
│ N │ │ n │ │ n │ │ Co │ │ nc │ │ERN │
│ C │ │ c │ │ c │ │ │ │ │ │ │
│ E │ │ e │ │ e │ │ co │ │ NC │ │ERN │
│ R │ │ r │ │ r │ │ │ │ │ │ │
│ N │ │ n │ │ n │ │ CO │ │ Nc │ │ern │
│ │ │ │ │ │ │ │ │ │ │ │
└────┘ └────┘ └────┘ └────┘ └────┘ └────┘
</code></pre>
</div>
</div>
<figcaption><p>What concerns?</p></figcaption>
</figure>
<p>To show that it isn’t just naming between concerns that can get you
into trouble, consider another small change to our HTML that
demonstrates the problems with our separation of concerns: imagine that
we decide to change the number field from an <code>&lt;output&gt;</code>
tag to an <code>&lt;input type="number"&gt;</code>.</p>
<p>This small change to our HTML will break our JavaScript, despite the
fact we have “separated” our concerns.</p>
<p>The fix for this issue is simple enough (we would need to change the
<code>.textContent</code> property to <code>.value</code> property), but
it demonstrates the burden of synchronizing markup changes and code
changes across multiple files. Keeping everything in sync can become
increasingly difficult as your application size increases.</p>
<p>The fact that small changes to our HTML can break our scripting
indicates that the two are <em class="test">tightly coupled</em>, despite being
broken up into multiple files. This tight coupling suggests that
separation between HTML and JavaScript (and CSS) is often an illusory
separation of concerns: the concerns are sufficiently related to one
another that they aren’t easily separated.</p>
<p>In Contact.app we are not <em class="test">concerned</em> with “structure,”
“styling” or “behavior”; we are concerned with collecting contact info
and presenting it to users. SoC, in the way it’s formulated in web
development orthodoxy, is not really an inviolate architectural
guideline, but rather a stylistic choice that, as we can see, can even
become a hindrance.</p>
<h4 id="locality-of-behavior">Locality of Behavior</h4>
<p>It turns out that there is a burgeoning reaction <em class="test">against</em> the
Separation of Concerns design principle. Consider the following web
technologies and techniques:</p>
<ul>
<li><p>JSX</p></li>
<li><p>LitHTML</p></li>
<li><p>CSS-in-JS</p></li>
<li><p>Single-File Components</p></li>
<li><p>Filesystem based routing</p></li>
</ul>
<p>Each of these technologies <em class="test">colocate</em> code in various
languages that address a single <em class="test">feature</em> (typically a UI
widget).</p>
<p>All of them mix <em class="test">implementation</em> concerns together in order to
present a unified abstraction to the end-user. Separating technical
detail concerns just isn’t as much of an, ahem, concern.</p>
<p>Locality of Behavior (LoB) is an alternative software design
principle that we coined, in opposition to Separation of Concerns. It
describes the following characteristic of a piece of software:</p>
<blockquote>
<p>The behavior of a unit of code should be as obvious as possible by
looking only at that unit of code.</p>
</blockquote><p class="quote-attribution"> https://htmx.org/essays/locality-of-behaviour/</p>
<p>In simple terms: you should be able to tell what a button does by
simply looking at the code or markup that creates that button. This does
not mean you need to inline the entire implementation, but that you
shouldn’t need to hunt for it or require prior knowledge of the codebase
to find it.</p>
<p>We will demonstrate Locality of Behavior in all of our examples, both
the counter demos and the features we add to Contact.app. Locality of
behavior is an explicit design goal of both _hyperscript and Alpine.js
(which we will cover later) as well as htmx.</p>
<p>All of these tools achieve Locality of Behavior by having you embed
attributes directly within your HTML, as opposed to having code look up
elements in a document through CSS selectors in order to add event
listeners onto them.</p>
<p>In a Hypermedia-Driven Application, we feel that the Locality of
Behavior design principle is often more important than the more
traditional Separation of Concerns design principle.</p>
<h4 id="what-to-do-with-our-counter-">What to do with our counter?</h4>
<p>So, should we go back to the <code>onclick</code> attribute way of
doing things? That approach certainly wins in Locality of Behavior, and
has the additional benefit that it is baked into HTML.</p>
<p>Unfortunately, however, the <code>on*</code> JavaScript attributes
also come with some drawbacks:</p>
<ul>
<li><p>They don’t support custom events.</p></li>
<li><p>There is no good mechanism for associating long-lasting variables
with an element — all variables are discarded when an event listener
completes executing.</p></li>
<li><p>If you have multiple instances of an element, you will need to
repeat the listener code on each, or use something more clever like
event delegation.</p></li>
<li><p>JavaScript code that directly manipulates the DOM gets verbose,
and clutters the markup.</p></li>
<li><p>An element cannot listen for events on another element.</p></li>
</ul>
<p>Consider this common situation: you have a popup, and you want it to
be dismissed when a user clicks outside of it. The listener will need to
be on the body element in this situation, far away from the actual popup
markup. This means that the body element would need to have listeners
attached to it that deal with many unrelated components. Some of these
components may not even be on the page when it was first rendered, if
they are added dynamically after the initial HTML page is rendered.</p>
<p>So vanilla JavaScript and Locality of Behavior don’t seem to mesh
<em class="test">quite</em> as well as we would like them to.</p>
<p>The situation is not hopeless, however: it’s important to understand
that LoB does not require behavior to be <em class="test">implemented</em> at a use
site, but merely <em class="test">invoked</em> there. That is, we don’t need to write
all our code on a given element, we just need to make it clear that a
given element is <em class="test">invoking</em> some code, which can be located
elsewhere.</p>
<p>Keeping this in mind, it <em class="test">is</em> possible to improve LoB while
writing JavaScript in a separate file, provided we have a reasonable
system for structuring our JavaScript.</p>
<h3 id="rsjs">RSJS</h3>
<p>(the “Reasonable System for JavaScript Structure,” <a href="https://ricostacruz.com/rsjs/">https://ricostacruz.com/rsjs/</a>)
is a set of guidelines for JavaScript architecture targeted at “a
typical non-SPA website.” RSJS provides a solution to the lack of a
standard code style for vanilla JavaScript that we mentioned
earlier.</p>
<p>Here are the RSJS guidelines most relevant for our counter
widget:</p>
<ul>
<li><p>“Use <code>data-</code> attributes” in HTML: invoking behavior
via adding data attributes makes it obvious there is JavaScript
happening, as opposed to using random classes or IDs that may be
mistakenly removed or changed.</p></li>
<li><p>“One component per file”: the name of the file should match the
data attribute so that it can be found easily, a win for LoB.</p></li>
</ul>
<p>To follow the RSJS guidelines, let’s restructure our current HTML and
JavaScript files. First, we will use <em class="test">data attributes</em>, that is,
HTML attributes that begin with <code>data-</code>, a standard feature
of HTML, to indicate that our HTML is a counter component. We will then
update our JavaScript to use an attribute selector that looks for the
<code>data-counter</code> attribute as the root element in our counter
component and wires in the appropriate event handlers and logic.
Additionally, let’s rework the code to use
<code>querySelectorAll()</code> and add the counter functionality to
<em class="test">all</em> counter components found on the page. (You never know how
many counters you might want!)</p>
<p>Here is what our code looks like now:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">section</span><span class="ot"> class</span><span class="op">=</span><span class="st">"counter"</span><span class="ot"> data-counter</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">output</span><span class="ot"> id</span><span class="op">=</span><span class="st">"my-output"</span><span class="ot"> data-counter-output</span><span class="dt">&gt;</span>0<span class="dt">&lt;/</span><span class="kw">output</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> class</span><span class="op">=</span><span class="st">"increment-btn"</span><span class="ot"> data-counter-increment</span><span class="dt">&gt;</span>Increment<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">section</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Counter in vanilla JavaScript, with RSJS</p></figcaption>
</figure>
<ol>
<li><p>Invoke a JavaScript behavior with a data attribute.</p></li>
<li><p>Mark relevant descendant elements.</p></li>
</ol>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="co">// counter.js &lt;1&gt;</span></span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a><span class="bu">document</span><span class="op">.</span><span class="fu">querySelectorAll</span>(<span class="st">"[data-counter]"</span>) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a> <span class="op">.</span><span class="fu">forEach</span>(el <span class="kw">=&gt;</span> {</span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a> <span class="kw">const</span></span>
<span id="cb6-5"><a aria-hidden="true" href="#cb6-5" tabindex="-1"></a> output <span class="op">=</span> el<span class="op">.</span><span class="fu">querySelector</span>(<span class="st">"[data-counter-output]"</span>)<span class="op">,</span></span>
<span id="cb6-6"><a aria-hidden="true" href="#cb6-6" tabindex="-1"></a> increment <span class="op">=</span> el<span class="op">.</span><span class="fu">querySelector</span>(<span class="st">"[data-counter-increment]"</span>)<span class="op">;</span> <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb6-7"><a aria-hidden="true" href="#cb6-7" tabindex="-1"></a></span>
<span id="cb6-8"><a aria-hidden="true" href="#cb6-8" tabindex="-1"></a> increment<span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"click"</span><span class="op">,</span> e <span class="kw">=&gt;</span> output<span class="op">.</span><span class="at">textContent</span><span class="op">++</span>)<span class="op">;</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span>
<span id="cb6-9"><a aria-hidden="true" href="#cb6-9" tabindex="-1"></a> })<span class="op">;</span></span></code></pre></div>
</figure>
<ol>
<li><p>File should have the same name as the data attribute, so that we
can locate it easily.</p></li>
<li><p>Get all elements that invoke this behavior.</p></li>
<li><p>Get any child elements we need.</p></li>
<li><p>Register event handlers.</p></li>
</ol>
<p>Using RSJS solves, or at least alleviates, many of the problems we
pointed out with our first, unstructured example of VanillaJS being
split out to a separate file:</p>
<ul>
<li><p>The JS that attaches behavior to a given element is
<em class="test">clear</em> (though only through naming conventions).</p></li>
<li><p>Reuse is <em class="test">easy</em> — you can create another counter component
on the page and it will just work.</p></li>
<li><p>The code is <em class="test">well-organized</em> — one behavior per
file.</p></li>
</ul>
<p>All in all, RSJS is a good way to structure your vanilla JavaScript
in a Hypermedia-Driven Application. So long as the JavaScript isn’t
communicating with a server via a plain data JSON API, or holding a
bunch of internal state outside of the DOM, this is perfectly compatible
with the HDA approach.</p>
<p>Let’s implement a feature in Contact.app using the RSJS/vanilla
JavaScript approach.</p>
<h3 id="_vanillajs_in_action_an_overflow_menu">VanillaJS in Action: An
Overflow Menu</h3>
<p>Our homepage has “Edit”, “View” and “Delete” links for every contact
in our table. This uses a lot of space and creates visual clutter. Let’s
fix that by placing these actions inside a drop-down menu with a button
to open it.</p>
<p>If you’re less familiar with JavaScript and the code here starts to
feel too complicated, don’t worry; the Alpine.js and _hyperscript
examples — which we’ll look at next — are easier to follow.</p>
<p>Let’s begin by sketching the markup we want for our dropdown menu.
First, we need an element, we’ll use a <code>&lt;div&gt;</code>, to
enclose the entire widget and mark it as a menu component. Within this
div, we will have a standard <code>&lt;button&gt;</code> that will
function as the mechanism that shows and hides our menu items. Finally,
we’ll have another <code>&lt;div&gt;</code> that holds the menu items
that we are going to show.</p>
<p>These menu items will be simple anchor tags, as they are in the
current contacts table.</p>
<p>Here is what our updated, RSJS-structured HTML looks like:</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> data-overflow-menu</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">"button"</span><span class="ot"> aria-haspopup</span><span class="op">=</span><span class="st">"menu"</span></span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a><span class="ot"> aria-controls</span><span class="op">=</span><span class="st">"contact-menu-{{ contact.id }}"</span></span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a><span class="ot"> </span><span class="dt">&gt;</span>Options<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb7-5"><a aria-hidden="true" href="#cb7-5" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> role</span><span class="op">=</span><span class="st">"menu"</span><span class="ot"> hidden id</span><span class="op">=</span><span class="st">"contact-menu-{{ contact.id }}"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>3&gt;</span>
<span id="cb7-6"><a aria-hidden="true" href="#cb7-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> role</span><span class="op">=</span><span class="st">"menuitem"</span></span>
<span id="cb7-7"><a aria-hidden="true" href="#cb7-7" tabindex="-1"></a><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}/edit"</span><span class="dt">&gt;</span>Edit<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span> <span class="er">&lt;</span>4&gt;</span>
<span id="cb7-8"><a aria-hidden="true" href="#cb7-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> role</span><span class="op">=</span><span class="st">"menuitem"</span><span class="ot"> href</span><span class="op">=</span><span class="st">"/contacts/{{ contact.id }}"</span><span class="dt">&gt;</span>View<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span id="cb7-9"><a aria-hidden="true" href="#cb7-9" tabindex="-1"></a> <span class="co">&lt;!-- ... --&gt;</span></span>
<span id="cb7-10"><a aria-hidden="true" href="#cb7-10" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb7-11"><a aria-hidden="true" href="#cb7-11" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Mark the root element of the menu component</p></li>
<li><p>This button will open and close our menu</p></li>
<li><p>A container for our menu items</p></li>
<li><p>Menu items</p></li>
</ol>
<p>The roles and ARIA attributes are based on the Menu and Menu Button
patterns from the ARIA Authoring Practices Guide.</p>
<div id="sidebar">
<div>
<div>
<p><strong>What is ARIA?</strong></p>
</div>
<div>
<p>As we web developers create more interactive, app-like websites,
HTML’s repertoire of elements won’t have all we need. As we have seen,
using CSS and JavaScript, we can endow existing elements with extended
behavior and appearances, rivaling those of native controls.</p>
<p>However, there was one thing web apps couldn’t replicate. While these
widgets may <em class="test">look</em> similar enough to the real deal, assistive
technology (e.g., screen readers) could only deal with the underlying
HTML elements.</p>
<p>Even if you take the time to get all the keyboard interactions right,
some users often are unable to work with these custom elements
easily.</p>
<p>ARIA was created by W3C’s Web Accessibility Initiative (WAI) in 2008
to address this problem. At a surface level, it is a set of attributes
you can add to HTML to make it meaningful to assistive software such as
a screen reader.</p>
<p>ARIA has two main components that interact with one another:</p>
<p>The first is the <code>role</code> attribute. This attribute has a
predefined set of possible values: <code>menu, dialog, radiogroup</code>
etc. The <code>role</code> attribute <em class="test">does not add any behavior</em>
to HTML elements. Rather, it is a promise you make to the user. When you
annotate an element as <code>role='menu'</code>, you are saying: <em class="test">I
will make this element work like a menu.</em></p>
<p>If you add a <code>role</code> to an element but you <em class="test">don’t</em>
uphold the promise, the experience for many users will be <em class="test">worse</em>
than if the element had no <code>role</code> at all. Thus, it is
written:</p>
<blockquote>
<p>No ARIA is better than Bad ARIA.</p>
</blockquote><p class="quote-attribution"> W3C, Read Me First | APG,
https://www.w3.org/WAI/ARIA/apg/practices/read-me-first/</p>
<p>The second component of ARIA is the <em class="test">states and properties</em>,
all sharing the <code>aria-</code> prefix:
<code>aria-expanded, aria-controls, aria-label</code> etc. These
attributes can specify various things such as the state of a widget, the
relationships between components, or additional semantics. Once again,
these attributes are <em class="test">promises</em>, not implementations.</p>
<p>Rather than learn all the roles and attributes and try to combine
them into a usable widget, the best course of action for most developers
is to rely on the ARIA Authoring Practices Guide (APG), a web resource
with practical information aimed directly at web developers.</p>
<p>If you’re new to ARIA, check out the following W3C resources:</p>
<ul>
<li><p>ARIA: Read Me First: <a href="https://www.w3.org/WAI/ARIA/apg/practices/read-me-first/">https://www.w3.org/WAI/ARIA/apg/practices/read-me-first/</a></p></li>
<li><p>ARIA UI patterns: <a href="https://www.w3.org/WAI/ARIA/apg/patterns/">https://www.w3.org/WAI/ARIA/apg/patterns/</a></p></li>
<li><p>ARIA Good Practices: <a href="https://www.w3.org/WAI/ARIA/apg/practices/">https://www.w3.org/WAI/ARIA/apg/practices/</a></p></li>
</ul>
<p>Always remember to <strong>test</strong> your website for
accessibility to ensure all users can interact with it easily and
effectively.</p>
</div>
</div>
</div>
<p>On the JS side of our implementation, we’ll begin with the RSJS
boilerplate: query for all elements with some data attribute, iterate
over them, get any relevant descendants.</p>
<p>Note that, below, we’ve modified the RSJS boilerplate a bit to
integrate with htmx; we load the overflow menu when htmx loads new
content.</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="kw">function</span> <span class="fu">overflowMenu</span>(tree <span class="op">=</span> <span class="bu">document</span>) {</span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a> tree<span class="op">.</span><span class="fu">querySelectorAll</span>(<span class="st">"[data-overflow-menu]"</span>)<span class="op">.</span><span class="fu">forEach</span>(menuRoot <span class="kw">=&gt;</span> { <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a> <span class="kw">const</span></span>
<span id="cb8-4"><a aria-hidden="true" href="#cb8-4" tabindex="-1"></a> button <span class="op">=</span> menuRoot<span class="op">.</span><span class="fu">querySelector</span>(<span class="st">"[aria-haspopup]"</span>)<span class="op">,</span> <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb8-5"><a aria-hidden="true" href="#cb8-5" tabindex="-1"></a> menu <span class="op">=</span> menuRoot<span class="op">.</span><span class="fu">querySelector</span>(<span class="st">"[role=menu]"</span>)<span class="op">,</span> <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb8-6"><a aria-hidden="true" href="#cb8-6" tabindex="-1"></a> items <span class="op">=</span> [<span class="op">...</span>menu<span class="op">.</span><span class="fu">querySelectorAll</span>(<span class="st">"[role=menuitem]"</span>)]<span class="op">;</span></span>
<span id="cb8-7"><a aria-hidden="true" href="#cb8-7" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb8-8"><a aria-hidden="true" href="#cb8-8" tabindex="-1"></a>}</span>
<span id="cb8-9"><a aria-hidden="true" href="#cb8-9" tabindex="-1"></a></span>
<span id="cb8-10"><a aria-hidden="true" href="#cb8-10" tabindex="-1"></a><span class="fu">addEventListener</span>(<span class="st">"htmx:load"</span><span class="op">,</span> e <span class="kw">=&gt;</span> <span class="fu">overflowMenu</span>(e<span class="op">.</span><span class="at">target</span>))<span class="op">;</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>With RSJS, you’ll be writing
<code>document.querySelectorAll(…​).forEach</code> a lot.</p></li>
<li><p>To keep the HTML clean, we use ARIA attributes rather than custom
data attributes here.</p></li>
<li><p>Use the spread operator to convert a <code>NodeList</code> into a
normal <code>Array</code>.</p></li>
<li><p>Initialize all overflow menus when the page is loaded or content
is inserted by htmx.</p></li>
</ol>
<p>Conventionally, we would keep track of whether the menu is open using
a JavaScript variable or a property in a JavaScript state object. This
approach is common in large, JavaScript-heavy web applications.</p>
<p>However, this approach has some drawback:</p>
<ul>
<li><p>We would need to keep the DOM in sync with the state (harder
without a framework).</p></li>
<li><p>We would lose the ability to serialize the HTML (as this open
state isn’t stored in the DOM, but rather in JavaScript).</p></li>
</ul>
<p>Instead of taking this approach, we will use the DOM to store our
state. We’ll lean on the <code>hidden</code> attribute on the menu
element to tell us it’s closed. If the HTML of the page is snapshotted
and restored, the menu can be restored as well by simply re-running the
JS.</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a>items <span class="op">=</span> [<span class="op">...</span>menu<span class="op">.</span><span class="fu">querySelectorAll</span>(<span class="st">"[role=menuitem]"</span>)]<span class="op">;</span> <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a><span class="kw">const</span> isOpen <span class="op">=</span> () <span class="kw">=&gt;</span> <span class="op">!</span>menu<span class="op">.</span><span class="at">hidden</span><span class="op">;</span> <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>We get the list of menu items at the start. This implementation
will not support dynamically adding or removing menu items.</p></li>
<li><p>The <code>hidden</code> attribute is helpfully reflected as a
<code>hidden</code> <em class="test">property</em>, so we don’t need to use
<code>getAttribute</code>.</p></li>
</ol>
<p>We’ll also make the menu items non-tabbable, so we can manage their
focus ourselves.</p>
<figure>
<div class="sourceCode" id="cb10"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb10-1"><a aria-hidden="true" href="#cb10-1" tabindex="-1"></a>items<span class="op">.</span><span class="fu">forEach</span>(item <span class="kw">=&gt;</span> item<span class="op">.</span><span class="fu">setAttribute</span>(<span class="st">"tabindex"</span><span class="op">,</span> <span class="st">"-1"</span>))<span class="op">;</span></span></code></pre></div>
</figure>
<p>Now let’s implement toggling the menu in JavaScript:</p>
<figure>
<div class="sourceCode" id="cb11"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb11-1"><a aria-hidden="true" href="#cb11-1" tabindex="-1"></a><span class="kw">function</span> <span class="fu">toggleMenu</span>(open <span class="op">=</span> <span class="op">!</span><span class="fu">isOpen</span>()) { <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb11-2"><a aria-hidden="true" href="#cb11-2" tabindex="-1"></a> <span class="cf">if</span> (open) {</span>
<span id="cb11-3"><a aria-hidden="true" href="#cb11-3" tabindex="-1"></a> menu<span class="op">.</span><span class="at">hidden</span> <span class="op">=</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb11-4"><a aria-hidden="true" href="#cb11-4" tabindex="-1"></a> button<span class="op">.</span><span class="fu">setAttribute</span>(<span class="st">"aria-expanded"</span><span class="op">,</span> <span class="st">"true"</span>)<span class="op">;</span></span>
<span id="cb11-5"><a aria-hidden="true" href="#cb11-5" tabindex="-1"></a> items[<span class="dv">0</span>]<span class="op">.</span><span class="fu">focus</span>()<span class="op">;</span> <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb11-6"><a aria-hidden="true" href="#cb11-6" tabindex="-1"></a> } <span class="cf">else</span> {</span>
<span id="cb11-7"><a aria-hidden="true" href="#cb11-7" tabindex="-1"></a> menu<span class="op">.</span><span class="at">hidden</span> <span class="op">=</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb11-8"><a aria-hidden="true" href="#cb11-8" tabindex="-1"></a> button<span class="op">.</span><span class="fu">setAttribute</span>(<span class="st">"aria-expanded"</span><span class="op">,</span> <span class="st">"false"</span>)<span class="op">;</span></span>
<span id="cb11-9"><a aria-hidden="true" href="#cb11-9" tabindex="-1"></a> }</span>
<span id="cb11-10"><a aria-hidden="true" href="#cb11-10" tabindex="-1"></a>}</span>
<span id="cb11-11"><a aria-hidden="true" href="#cb11-11" tabindex="-1"></a></span>
<span id="cb11-12"><a aria-hidden="true" href="#cb11-12" tabindex="-1"></a><span class="fu">toggleMenu</span>(<span class="fu">isOpen</span>())<span class="op">;</span> <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb11-13"><a aria-hidden="true" href="#cb11-13" tabindex="-1"></a>button<span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"click"</span><span class="op">,</span> () <span class="kw">=&gt;</span> <span class="fu">toggleMenu</span>())<span class="op">;</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span>
<span id="cb11-14"><a aria-hidden="true" href="#cb11-14" tabindex="-1"></a>menuRoot<span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"blur"</span><span class="op">,</span> e <span class="kw">=&gt;</span> <span class="fu">toggleMenu</span>(<span class="kw">false</span>))<span class="op">;</span> <span class="op">&lt;</span><span class="dv">5</span><span class="op">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Optional parameter to specify desired state. This allows us to
use one function to open, close, or toggle the menu.</p></li>
<li><p>Focus first item of menu when opened.</p></li>
<li><p>Call <code>toggleMenu</code> with current state, to initialize
element attributes.</p></li>
<li><p>Toggle menu when button is clicked.</p></li>
<li><p>Close menu when focus moves away.</p></li>
</ol>
<p>Let’s also make the menu close when we click outside it, a nice
behavior that mimics how native drop-down menus work. This will require
an event listener on the whole window.</p>
<p>Note that we need to be careful with this kind of listener: you may
find that listeners accumulate as components add listeners and fail to
remove them when the component is removed from the DOM. This,
unfortunately, leads to difficult to track down memory leaks.</p>
<p>There is not an easy way in JavaScript to execute logic when an
element is removed. The best option is what is known as the
<code>MutationObserver</code> API. A <code>MutationObserver</code> is
very useful, but the API is quite heavy and a bit arcane, so we won’t be
using it for our example.</p>
<p>Instead, we will use a simple pattern to avoid leaking event
listeners: when our event listener runs, we will check if the attaching
component is still in the DOM, and, if the element is no longer in the
DOM, we will remove the listener and exit.</p>
<p>This is a somewhat hacky, manual form of <em class="test">garbage collection</em>.
As is (usually) the case with other garbage collection algorithms, our
strategy removes listeners in a nondeterministic amount of time after
they are no longer needed. Fortunately for us, With a frequent event
like “the user clicks anywhere in the page” driving the collection, it
should work well enough for our system.</p>
<figure>
<div class="sourceCode" id="cb12"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb12-1"><a aria-hidden="true" href="#cb12-1" tabindex="-1"></a><span class="bu">window</span><span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"click"</span><span class="op">,</span> <span class="kw">function</span> <span class="fu">clickAway</span>(<span class="bu">event</span>) {</span>
<span id="cb12-2"><a aria-hidden="true" href="#cb12-2" tabindex="-1"></a> <span class="cf">if</span> (<span class="op">!</span>menuRoot<span class="op">.</span><span class="at">isConnected</span>)</span>
<span id="cb12-3"><a aria-hidden="true" href="#cb12-3" tabindex="-1"></a> <span class="bu">window</span><span class="op">.</span><span class="fu">removeEventListener</span>(<span class="st">"click"</span><span class="op">,</span> clickAway)<span class="op">;</span> <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb12-4"><a aria-hidden="true" href="#cb12-4" tabindex="-1"></a> <span class="cf">if</span> (<span class="op">!</span>menuRoot<span class="op">.</span><span class="fu">contains</span>(<span class="bu">event</span><span class="op">.</span><span class="at">target</span>)) <span class="fu">toggleMenu</span>(<span class="kw">false</span>)<span class="op">;</span> <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb12-5"><a aria-hidden="true" href="#cb12-5" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div>
</figure>
<ol>
<li><p>This line is the garbage collection.</p></li>
<li><p>If the click is outside the menu, close the menu.</p></li>
</ol>
<p>Now, let’s move on to the keyboard interactions for our dropdown
menu. The keyboard handlers turn out to all be pretty similar to one
another and not particularly intricate, so let’s knock them all out in
one go:</p>
<figure>
<div class="sourceCode" id="cb13"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb13-1"><a aria-hidden="true" href="#cb13-1" tabindex="-1"></a><span class="kw">const</span> currentIndex <span class="op">=</span> () <span class="kw">=&gt;</span> { <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb13-2"><a aria-hidden="true" href="#cb13-2" tabindex="-1"></a> <span class="kw">const</span> idx <span class="op">=</span> items<span class="op">.</span><span class="fu">indexOf</span>(<span class="bu">document</span><span class="op">.</span><span class="at">activeElement</span>)<span class="op">;</span></span>
<span id="cb13-3"><a aria-hidden="true" href="#cb13-3" tabindex="-1"></a> <span class="cf">if</span> (idx <span class="op">===</span> <span class="op">-</span><span class="dv">1</span>) <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb13-4"><a aria-hidden="true" href="#cb13-4" tabindex="-1"></a> <span class="cf">return</span> idx<span class="op">;</span></span>
<span id="cb13-5"><a aria-hidden="true" href="#cb13-5" tabindex="-1"></a>}</span>
<span id="cb13-6"><a aria-hidden="true" href="#cb13-6" tabindex="-1"></a></span>
<span id="cb13-7"><a aria-hidden="true" href="#cb13-7" tabindex="-1"></a>menu<span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"keydown"</span><span class="op">,</span> e <span class="kw">=&gt;</span> {</span>
<span id="cb13-8"><a aria-hidden="true" href="#cb13-8" tabindex="-1"></a> <span class="cf">if</span> (e<span class="op">.</span><span class="at">key</span> <span class="op">===</span> <span class="st">"ArrowUp"</span>) {</span>
<span id="cb13-9"><a aria-hidden="true" href="#cb13-9" tabindex="-1"></a> items[<span class="fu">currentIndex</span>() <span class="op">-</span> <span class="dv">1</span>]<span class="op">?.</span><span class="fu">focus</span>()<span class="op">;</span> <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb13-10"><a aria-hidden="true" href="#cb13-10" tabindex="-1"></a></span>
<span id="cb13-11"><a aria-hidden="true" href="#cb13-11" tabindex="-1"></a> } <span class="cf">else</span> <span class="cf">if</span> (e<span class="op">.</span><span class="at">key</span> <span class="op">===</span> <span class="st">"ArrowDown"</span>) {</span>
<span id="cb13-12"><a aria-hidden="true" href="#cb13-12" tabindex="-1"></a> items[<span class="fu">currentIndex</span>() <span class="op">+</span> <span class="dv">1</span>]<span class="op">?.</span><span class="fu">focus</span>()<span class="op">;</span> <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb13-13"><a aria-hidden="true" href="#cb13-13" tabindex="-1"></a></span>
<span id="cb13-14"><a aria-hidden="true" href="#cb13-14" tabindex="-1"></a> } <span class="cf">else</span> <span class="cf">if</span> (e<span class="op">.</span><span class="at">key</span> <span class="op">===</span> <span class="st">"Space"</span>) {</span>
<span id="cb13-15"><a aria-hidden="true" href="#cb13-15" tabindex="-1"></a> items[<span class="fu">currentIndex</span>()]<span class="op">.</span><span class="fu">click</span>()<span class="op">;</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span>
<span id="cb13-16"><a aria-hidden="true" href="#cb13-16" tabindex="-1"></a></span>
<span id="cb13-17"><a aria-hidden="true" href="#cb13-17" tabindex="-1"></a> } <span class="cf">else</span> <span class="cf">if</span> (e<span class="op">.</span><span class="at">key</span> <span class="op">===</span> <span class="st">"Home"</span>) {</span>
<span id="cb13-18"><a aria-hidden="true" href="#cb13-18" tabindex="-1"></a> items[<span class="dv">0</span>]<span class="op">.</span><span class="fu">focus</span>()<span class="op">;</span> <span class="op">&lt;</span><span class="dv">5</span><span class="op">&gt;</span></span>
<span id="cb13-19"><a aria-hidden="true" href="#cb13-19" tabindex="-1"></a></span>
<span id="cb13-20"><a aria-hidden="true" href="#cb13-20" tabindex="-1"></a> } <span class="cf">else</span> <span class="cf">if</span> (e<span class="op">.</span><span class="at">key</span> <span class="op">===</span> <span class="st">"End"</span>) {</span>
<span id="cb13-21"><a aria-hidden="true" href="#cb13-21" tabindex="-1"></a> items[items<span class="op">.</span><span class="at">length</span> <span class="op">-</span> <span class="dv">1</span>]<span class="op">.</span><span class="fu">focus</span>()<span class="op">;</span> <span class="op">&lt;</span><span class="dv">6</span><span class="op">&gt;</span></span>
<span id="cb13-22"><a aria-hidden="true" href="#cb13-22" tabindex="-1"></a></span>
<span id="cb13-23"><a aria-hidden="true" href="#cb13-23" tabindex="-1"></a> } <span class="cf">else</span> <span class="cf">if</span> (e<span class="op">.</span><span class="at">key</span> <span class="op">===</span> <span class="st">"Escape"</span>) {</span>
<span id="cb13-24"><a aria-hidden="true" href="#cb13-24" tabindex="-1"></a> <span class="fu">toggleMenu</span>(<span class="kw">false</span>)<span class="op">;</span> <span class="op">&lt;</span><span class="dv">7</span><span class="op">&gt;</span></span>
<span id="cb13-25"><a aria-hidden="true" href="#cb13-25" tabindex="-1"></a> button<span class="op">.</span><span class="fu">focus</span>()<span class="op">;</span> <span class="op">&lt;</span><span class="dv">8</span><span class="op">&gt;</span></span>
<span id="cb13-26"><a aria-hidden="true" href="#cb13-26" tabindex="-1"></a> }</span>
<span id="cb13-27"><a aria-hidden="true" href="#cb13-27" tabindex="-1"></a>})<span class="op">;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Helper: Get the index in the items array of the currently focused
menu item (0 if none).</p></li>
<li><p>Move focus to the previous menu item when the up arrow key is
pressed.</p></li>
<li><p>Move focus to the next menu item when the down arrow key is
pressed.</p></li>
<li><p>Activate the currently focused element when the space key is
pressed.</p></li>
<li><p>Move focus to the first menu item when Home is pressed.</p></li>
<li><p>Move focus to the last menu item when End is pressed.</p></li>
<li><p>Close menu when Escape is pressed.</p></li>
<li><p>Return focus to menu button when closing menu.</p></li>
</ol>
<p>That should cover all our bases, and we’ll admit that’s a lot of
code. But, in fairness, it’s code that encodes a lot of behavior.</p>
<p>Now, our drop-down menu isn’t perfect, and it doesn’t handle a lot of
things. For example, we don’t support submenus, or menu items being
added or removed dynamically to the menu. If we needed more menu
features like this, it might make more sense to use an off-the-shelf
library, such as GitHub’s <a href="https://github.com/github/details-menu-element"><code>details-menu-element</code></a>.</p>
<p>But, for our relatively simple use case, vanilla JavaScript does a
fine job, and we got to explore ARIA and RSJS while implementing it.</p>
<h2 id="alpine-js">Alpine.js</h2>
<p>OK, so that’s an in-depth look at how to structure plain
VanillaJS-style JavaScript. Let’s turn our attention to an actual
JavaScript framework that enables a different approach for adding
dynamic behavior to your application, <a href="https://alpinejs.dev">Alpine.js</a>.</p>
<p>Alpine is a relatively new JavaScript library that allows developers
to embed JavaScript code directly in HTML, akin to the <code>on*</code>
attributes available in plain HTML and JavaScript. However, Alpine takes
this concept of embedded scripting much further than <code>on*</code>
attributes.</p>
<p>Alpine bills itself as a modern replacement for jQuery, the widely
used, older JavaScript library. As you will see, it definitely lives up
to this promise.</p>
<p>Installing Alpine is very easy: it is a single file and is
dependency-free, so you can simply include it via a CDN:</p>
<figure>
<div class="sourceCode" id="cb14"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb14-1"><a aria-hidden="true" href="#cb14-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">script</span><span class="ot"> src</span><span class="op">=</span><span class="st">"https://unpkg.com/alpinejs"</span><span class="dt">&gt;&lt;/</span><span class="kw">script</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Installing Alpine</p></figcaption>
</figure>
<p>You can also install it via a package manager such as NPM, or vendor
it from your own server.</p>
<p>Alpine provides a set of HTML attributes, all of which begin with the
<code>x-</code> prefix, the main one of which is <code>x-data</code>.
The content of <code>x-data</code> is a JavaScript expression which
evaluates to an object. The properties of this object can, then, be
accessed within the element that the <code>x-data</code> attribute is
located.</p>
<p>To get a flavor of AlpineJS, let’s look at how to implement our
counter example using it.</p>
<p>For the counter, the only state we need to keep track of is the
current number, so let’s declare a JavaScript object with one property,
<code>count</code>, in an <code>x-data</code> attribute on the div for
our counter:</p>
<figure>
<div class="sourceCode" id="cb15"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb15-1"><a aria-hidden="true" href="#cb15-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"counter"</span><span class="ot"> x-data</span><span class="op">=</span><span class="st">"{ count: 0 }"</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Counter with Alpine, line 1</p></figcaption>
</figure>
<p>This defines our state, that is, the data we are going to be using to
drive dynamic updates to the DOM. With the state declared like this, we
can now use it <em class="test">within</em> the div element it is declared on. Let’s
add an <code>output</code> element with an <code>x-text</code>
attribute.</p>
<p>Next, we will <em class="test">bind</em> the <code>x-text</code> attribute to the
<code>count</code> attribute we declared in the <code>x-data</code>
attribute on the parent <code>div</code> element. This will have the
effect of setting the text of the <code>output</code> element to
whatever the value of <code>count</code> is: if <code>count</code> is
updated, so will the text of the <code>output</code>. This is “reactive”
programming, in that the DOM will “react” to changes to the backing
data.</p>
<figure>
<div class="sourceCode" id="cb16"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb16-1"><a aria-hidden="true" href="#cb16-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> x-data</span><span class="op">=</span><span class="st">"{ count: 0 }"</span><span class="dt">&gt;</span></span>
<span id="cb16-2"><a aria-hidden="true" href="#cb16-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">output</span><span class="ot"> x-text</span><span class="op">=</span><span class="st">"count"</span><span class="dt">&gt;&lt;/</span><span class="kw">output</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
<figcaption><p>Counter with Alpine, lines 1-2</p></figcaption>
</figure>
<ol>
<li><p>The <code>x-text</code> attribute.</p></li>
</ol>
<p>Next, we need to update the count, using a button. Alpine allows you
to attach event listeners with the <code>x-on</code> attribute.</p>
<p>To specify the event to listen for, you add a colon and then the
event name after the <code>x-on</code> attribute name. Then, the value
of the attribute is the JavaScript you wish to execute. This is similar
to the plain <code>on*</code> attributes we discussed earlier, but it
turns out to be much more flexible.</p>
<p>We want to listen for a <code>click</code> event, and we want to
increment <code>count</code> when a click occurs, so here is what the
Alpine code will look like:</p>
<figure>
<div class="sourceCode" id="cb17"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb17-1"><a aria-hidden="true" href="#cb17-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> x-data</span><span class="op">=</span><span class="st">"{ count: 0 }"</span><span class="dt">&gt;</span></span>
<span id="cb17-2"><a aria-hidden="true" href="#cb17-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">output</span><span class="ot"> x-text</span><span class="op">=</span><span class="st">"count"</span><span class="dt">&gt;&lt;/</span><span class="kw">output</span><span class="dt">&gt;</span></span>
<span id="cb17-3"><a aria-hidden="true" href="#cb17-3" tabindex="-1"></a></span>
<span id="cb17-4"><a aria-hidden="true" href="#cb17-4" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> x-on:click</span><span class="op">=</span><span class="st">"count++"</span><span class="dt">&gt;</span>Increment<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb17-5"><a aria-hidden="true" href="#cb17-5" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Counter with Alpine, the full thing</p></figcaption>
</figure>
<ol>
<li><p>With <code>x-on</code>, we specify the event in the attribute
<em class="test">name</em>.</p></li>
</ol>
<p>And that’s all it takes. A simple component like a counter should be
simple to code, and Alpine delivers.</p>
<h3 id="-x-on-click--vs---onclick-">“x-on:click” vs. “onclick”</h3>
<p>As we said, the Alpine <code>x-on:click</code> attribute (or its
shorthand, the <code>@click</code> attribute) is similar to the built-in
<code>onclick</code> attribute. However, it has additional features that
make it significantly more useful:</p>
<ul>
<li><p>You can listen for events from other elements. For example, the
<code>.outside</code> modifier lets you listen to any click event that
is <em class="test">not</em> within the element.</p></li>
<li><p>You can use other modifiers to:</p>
<ul>
<li><p>throttle or debounce event listeners</p></li>
<li><p>ignore events that are bubbled up from descendant
elements</p></li>
<li><p>attach passive listeners</p></li>
</ul></li>
<li><p>You can listen to custom events. For example, if you wanted to
listen for the <code>htmx:after-request</code> event you could write
<code>x-on:htmx:after-request="doSomething()"</code>.</p></li>
</ul>
<h3 id="reactivity-and-templating">Reactivity and Templating</h3>
<p>We hope you’ll agree that the AlpineJS version of the counter widget
is better, in general, than the VanillaJS implementation, which was
either somewhat hacky or spread out over multiple files.</p>
<p>A big part of the power of AlpineJS is that it supports a notion of
“reactive” variables, allowing you to bind the count of the
<code>div</code> element to a variable that both the <code>output</code>
and the <code>button</code> can reference, and properly updating all the
dependencies when a mutation occurs. Alpine allows for much more
elaborate data bindings than we have demonstrated here, and it is an
excellent general purpose client-side scripting library.</p>
<h3 id="_alpine_js_in_action_a_bulk_action_toolbar">Alpine.js in Action:
A Bulk Action Toolbar</h3>
<p>Let’s implement a feature in Contact.app with Alpine. As it stands
currently, Contact.app has a “Delete Selected Contacts” button at the
very bottom of the page. This button has a long name, is not easy to
find and takes up a lot of room. If we wanted to add additional “bulk”
actions, this wouldn’t scale well visually.</p>
<p>In this section, we’ll replace this single button with a toolbar.
Furthermore, the toolbar will only appear when the user starts selecting
contacts. Finally, it will show how many contacts are selected and let
you select all contacts in one go.</p>
<p>The first thing we will need to add is an <code>x-data</code>
attribute, to hold the state that we will use to determine if the
toolbar is visible or not. We will need to place this on an ancestor
element of both the toolbar that we are going to add, as well as the
checkboxes, which will be updating the state when they are checked and
unchecked. The best option given our current HTML is to place the
attribute on the <code>form</code> element that surrounds the contacts
table. We will declare a property, <code>selected</code>, which will be
an array that holds the selected contact ids, based on the checkboxes
that are selected.</p>
<p>Here is what our form tag will look like:</p>
<figure>
<div class="sourceCode" id="cb18"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb18-1"><a aria-hidden="true" href="#cb18-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> x-data</span><span class="op">=</span><span class="st">"{ selected: [] }"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
</figure>
<ol>
<li><p>This form wraps around the contacts table.</p></li>
</ol>
<p>Next, at the top of the contacts table, we are going to add a
<code>template</code> tag. A template tag is <em class="test">not</em> rendered by a
browser, by default, so you might be surprised that we are using it.
However, by adding an Alpine <code>x-if</code> attribute, we can tell
Alpine: if a condition is true, show the HTML within this template.</p>
<p>Recall that we want to show the toolbar if and only if one or more
contacts are selected. But we know that we will have the ids of the
selected contacts in the <code>selected</code> property. Therefore, we
can check the <em class="test">length</em> of that array to see if there are any
selected contacts, quite easily:</p>
<figure>
<div class="sourceCode" id="cb19"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb19-1"><a aria-hidden="true" href="#cb19-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">template</span><span class="ot"> x-if</span><span class="op">=</span><span class="st">"selected.length &gt; 0"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb19-2"><a aria-hidden="true" href="#cb19-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"box info tool-bar"</span><span class="dt">&gt;</span></span>
<span id="cb19-3"><a aria-hidden="true" href="#cb19-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">slot</span><span class="ot"> x-text</span><span class="op">=</span><span class="st">"selected.length"</span><span class="dt">&gt;&lt;/</span><span class="kw">slot</span><span class="dt">&gt;</span></span>
<span id="cb19-4"><a aria-hidden="true" href="#cb19-4" tabindex="-1"></a> contacts selected</span>
<span id="cb19-5"><a aria-hidden="true" href="#cb19-5" tabindex="-1"></a></span>
<span id="cb19-6"><a aria-hidden="true" href="#cb19-6" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">"button"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"bad bg color border"</span><span class="dt">&gt;</span>Delete<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb19-7"><a aria-hidden="true" href="#cb19-7" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">hr</span><span class="ot"> aria-orientation</span><span class="op">=</span><span class="st">"vertical"</span><span class="dt">&gt;</span></span>
<span id="cb19-8"><a aria-hidden="true" href="#cb19-8" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">"button"</span><span class="dt">&gt;</span>Cancel<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span> <span class="er">&lt;</span>2&gt;</span>
<span id="cb19-9"><a aria-hidden="true" href="#cb19-9" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span id="cb19-10"><a aria-hidden="true" href="#cb19-10" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">template</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Show this HTML if there are 1 or more selected contacts.</p></li>
<li><p>We will implement these buttons in just a moment.</p></li>
</ol>
<p>The next step is to ensure that toggling a checkbox for a given
contact adds (or removes) a given contact’s id from the
<code>selected</code> property. To do this, we will need to use a new
Alpine attribute, <code>x-model</code>. The <code>x-model</code>
attribute allows you to <em class="test">bind</em> a given element to some underlying
data, or its “model.”</p>
<p>In this case, we want to bind the value of the checkbox inputs to the
<code>selected</code> property. This is how we do this:</p>
<figure>
<div class="sourceCode" id="cb20"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb20-1"><a aria-hidden="true" href="#cb20-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span id="cb20-2"><a aria-hidden="true" href="#cb20-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">"checkbox"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"selected_contact_ids"</span></span>
<span id="cb20-3"><a aria-hidden="true" href="#cb20-3" tabindex="-1"></a><span class="ot"> value</span><span class="op">=</span><span class="st">"{{ contact.id }}"</span><span class="ot"> x-model</span><span class="op">=</span><span class="st">"selected"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb20-4"><a aria-hidden="true" href="#cb20-4" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>The <code>x-model</code> attribute binds the <code>value</code>
of this input to the <code>selected</code> property</p></li>
</ol>
<p>Now, when a checkbox is checked or unchecked, the
<code>selected</code> array will be updated with the given row’s contact
id. Furthermore, mutations we make to the <code>selected</code> array
will similarly be reflected in the checkboxes” state. This is known as a
<em class="test">two-way</em> binding.</p>
<p>With this code written, we can make the toolbar appear and disappear,
based on whether contact checkboxes are selected.</p>
<p>Very slick.</p>
<p>Before we move on, you may have noticed our code here includes some
“class=” references. These are for css styling, and are not part of
Alpine.js. We’ve included them only as a reminder that the menu bar
we’re building will require css to work well. The classes in the code
above refer to a minimal css library called Missing.css. If you use
other css libraries, such as Bootstrap, Tailwind, Bulma, Pico.css, etc.,
your styling code will be different.</p>
<h4 id="_implementing_actions">Implementing actions</h4>
<p>Now that we have the mechanics of showing and hiding the toolbar,
let’s look at how to implement the buttons within the toolbar.</p>
<p>Let’s first implement the “Clear” button, because it is quite easy.
All we need to do is, when the button is clicked, clear out the
<code>selected</code> array. Because of the two-way binding that Alpine
provides, this will uncheck all the selected contacts (and then hide the
toolbar)!</p>
<p>For the <em class="test">Cancel</em> button, our job is simple:</p>
<figure>
<div class="sourceCode" id="cb21"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb21-1"><a aria-hidden="true" href="#cb21-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">"button"</span><span class="ot"> </span><span class="er">@</span><span class="ot">click</span><span class="op">=</span><span class="st">"selected = []"</span><span class="dt">&gt;</span>Cancel<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
</figure>
<ol>
<li><p>Reset the <code>selected</code> array.</p></li>
</ol>
<p>Once again, AlpineJS makes this very easy.</p>
<p>The “Delete” button, however, will be a bit more complicated. It will
need to do two things: first it will confirm if the user indeed intends
to delete the contacts selected. Then, if the user confirms the action,
it will use the htmx JavaScript API to issue a <code>DELETE</code>
request.</p>
<figure>
<div class="sourceCode" id="cb22"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb22-1"><a aria-hidden="true" href="#cb22-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">"button"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"bad bg color border"</span></span>
<span id="cb22-2"><a aria-hidden="true" href="#cb22-2" tabindex="-1"></a><span class="ot"> </span><span class="er">@</span><span class="ot">click</span><span class="op">=</span><span class="st">"</span></span>
<span id="cb22-3"><a aria-hidden="true" href="#cb22-3" tabindex="-1"></a><span class="st"> confirm(`Delete ${selected.length} contacts?`) </span><span class="er">&amp;&amp;</span><span class="st"> </span><span class="er">&lt;</span><span class="st">1&gt;</span></span>
<span id="cb22-4"><a aria-hidden="true" href="#cb22-4" tabindex="-1"></a><span class="st"> htmx.ajax('DELETE', '/contacts',</span></span>
<span id="cb22-5"><a aria-hidden="true" href="#cb22-5" tabindex="-1"></a><span class="st"> { source: $root, target: document.body }) </span><span class="er">&lt;</span><span class="st">2&gt;</span></span>
<span id="cb22-6"><a aria-hidden="true" href="#cb22-6" tabindex="-1"></a><span class="st"> "</span><span class="dt">&gt;</span></span>
<span id="cb22-7"><a aria-hidden="true" href="#cb22-7" tabindex="-1"></a> Delete</span>
<span id="cb22-8"><a aria-hidden="true" href="#cb22-8" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>Confirm the user wishes to delete the selected number of
contacts.</p></li>
<li><p>Issue a <code>DELETE</code> using the htmx JavaScript
API.</p></li>
</ol>
<p>Note that we are using the short-circuiting behavior of the
<code>&amp;&amp;</code> operator in JavaScript to avoid the call to
<code>htmx.ajax()</code> if the <code>confirm()</code> call returns
false.</p>
<p>The <code>htmx.ajax()</code> function is just a way to access the
normal, HTML-driven hypermedia exchange that htmx’s HTML attributes give
you directly from JavaScript.</p>
<p>Looking at how we call <code>htmx.ajax</code>, we first pass in that
we want to issue a <code>DELETE</code> to <code>/contacts</code>. We
then pass in two additional pieces of information: <code>source</code>
and <code>target</code>. The <code>source</code> property is the element
from which htmx will collect data to include in the request. We set this
to <code>$root</code>, which is a special symbol in Alpine that will be
the element that has the <code>x-data</code> attribute declared on it.
In this case, it will be the form containing all of our contacts. The
<code>target</code>, or where the response HTML will be placed, is just
the entire document’s body, since the <code>DELETE</code> handler
returns a whole page when it completes.</p>
<p>Note that we are using Alpine here in a Hypermedia-Driven Application
compatible manner. We <em class="test">could</em> have issued an AJAX request
directly from Alpine and perhaps updated an <code>x-data</code> property
depending on the results of that request. But, instead, we delegated to
htmx’s JavaScript API, which made a <em class="test">hypermedia exchange</em> with
the server.</p>
<p>This is the key to scripting in a hypermedia-friendly manner within a
Hypermedia-Driven Application.</p>
<p>So, with all of this in place, we now have a much improved experience
for performing bulk actions on contacts: less visual clutter and the
toolbar can be extended with more options without creating bloat in the
main interface of our app.</p>
<h2 id="-hyperscript">_hyperscript</h2>
<p>The final scripting technology we are going to look at is a bit
further afield: <a href="https://hyperscript.org">_hyperscript</a>. The
authors of this book initially created _hyperscript as a sibling project
to htmx. We felt that JavaScript wasn’t event-oriented enough, which
made adding small scripting enhancements to htmx applications
cumbersome.</p>
<p>While the previous two examples are JavaScript-oriented, _hyperscript
has a completely different syntax than JavaScript, based on an older
language called HyperTalk. HyperTalk was the scripting language for a
technology called HyperCard, an old hypermedia system available on early
Macintosh Computers.</p>
<p>The most noticeable thing about _hyperscript is that it resembles
English prose more than it resembles other programming languages.</p>
<p>Like Alpine, _hyperscript is a modern jQuery replacement. Also like
Alpine, _hyperscript allows you to write your scripting inline, in
HTML.</p>
<p>Unlike Alpine, however, _hyperscript is <em class="test">not</em> reactive. It
instead focuses on making DOM manipulations in response to events easy
to write and easy to read. It has built-in language constructs for many
DOM operations, preventing you from needing to navigate the
sometimes-verbose JavaScript DOM APIs.</p>
<p>We will give a small taste of what scripting in the _hyperscript
language is like, so you can pursue the language in more depth later if
you find it interesting.</p>
<p>Like htmx and AlpineJS, _hyperscript can be installed via a CDN or
from npm (package name <code>hyperscript.org</code>):</p>
<figure>
<div class="sourceCode" id="cb23"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb23-1"><a aria-hidden="true" href="#cb23-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">script</span><span class="ot"> src</span><span class="op">=</span><span class="st">"//unpkg.com/hyperscript.org"</span><span class="dt">&gt;&lt;/</span><span class="kw">script</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>Installing _hyperscript via CDN</p></figcaption>
</figure>
<p>_hyperscript uses the <code>_</code> (underscore) attribute for
putting scripting on DOM elements. You may also use the
<code>script</code> or <code>data-script</code> attributes, depending on
your HTML validation needs.</p>
<p>Let’s look at how to implement the simple counter component we have
been looking at using _hyperscript. We will place an <code>output</code>
element and a <code>button</code> inside of a <code>div</code>. To
implement the counter, we will need to add a small bit of _hyperscript
to the button. On a click, the button should increment the text of the
previous <code>output</code> tag.</p>
<p>As you’ll see, that last sentence is close to the actual _hyperscript
code:</p>
<figure>
<div class="sourceCode" id="cb24"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb24-1"><a aria-hidden="true" href="#cb24-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">"counter"</span><span class="dt">&gt;</span></span>
<span id="cb24-2"><a aria-hidden="true" href="#cb24-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">output</span><span class="dt">&gt;</span>0<span class="dt">&lt;/</span><span class="kw">output</span><span class="dt">&gt;</span></span>
<span id="cb24-3"><a aria-hidden="true" href="#cb24-3" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> _</span><span class="op">=</span><span class="st">"on click</span></span>
<span id="cb24-4"><a aria-hidden="true" href="#cb24-4" tabindex="-1"></a><span class="st"> increment the textContent of the previous </span><span class="er">&lt;</span><span class="st">output/&gt;"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span>
<span id="cb24-5"><a aria-hidden="true" href="#cb24-5" tabindex="-1"></a> Increment</span>
<span id="cb24-6"><a aria-hidden="true" href="#cb24-6" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span id="cb24-7"><a aria-hidden="true" href="#cb24-7" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<ol>
<li><p>The _hyperscript code added inline to the button.</p></li>
</ol>
<p>Let’s go through each component of this script:</p>
<ul>
<li><p><code>on click</code> is an event listener, telling the button to
listen for a <code>click</code> event and then executing the remaining
code.</p></li>
<li><p><code>increment</code> is a “command” in _hyperscript that
“increments” things, similar to the <code>++</code> operator in
JavaScript.</p></li>
<li><p><code>the</code> doesn’t have any semantic meaning in
_hyperscript, but can be used to make scripts more readable.</p></li>
<li><p><code>textContent of</code> is one form of <em class="test">property
access</em> in _hyperscript. You are probably familiar with the
JavaScript syntax <code>a.b</code>, meaning “Get the property
<code>b</code> on object <code>a</code>. _hyperscript supports this
syntax, but also supports the forms <code>b of a</code> and
<code>a’s b</code>. Which one you use should depend on which one is most
readable.</p></li>
<li><p><code>previous</code> is an expression in _hyperscript that finds
the previous element in the DOM that matches some condition.</p></li>
<li><p><code>&lt;output /&gt;</code> is a <em class="test">query literal</em>, which
is a CSS selector wrapped between <code>&lt;</code> and
<code>/&gt;</code>.</p></li>
</ul>
<p>In this code, the <code>previous</code> keyword (and the accompanying
<code>next</code> keyword) is an example of how _hyperscript makes DOM
operations easier: there is no such native functionality to be found in
the standard DOM API, and implementing this in VanillaJS is trickier
than you might think!</p>
<p>So, you can see, _hyperscript is very expressive, particularly when
it comes to DOM manipulations. This makes it easier to embed scripts
directly in HTML: since the scripting language is more powerful, scripts
written in it tend to be shorter and easier to read.</p>
<div id="sidebar">
<div>
<div>
<p><strong>Natural Language Programming?</strong></p>
</div>
<div>
<p>Seasoned programmers may be suspicious of _hyperscript: There have
been many “natural language programming” (NLP) projects that target
non-programmers and beginner programmers, assuming that being able to
read code in their “natural language” will give them the ability to
write it as well. This has led to some badly written and structured code
and has failed to live up to the (often over the top) hype.</p>
<p>_hyperscript is <em class="test">not</em> an NLP programming language. Yes, its
syntax is inspired in many places by the speech patterns of web
developers. But _hyperscript’s readability is achieved not through
complex heuristics or fuzzy NLP processing, but rather through judicious
use of common parsing tricks, coupled with a culture of readability.</p>
<p>As you can see in the above example, with the use of a <em class="test">query
reference</em>, <code>&lt;output/&gt;</code>, _hyperscript does not shy
away from using DOM-specific, non-natural language when appropriate.</p>
</div>
</div>
</div>
<h3 id="_hyperscript_in_action_a_keyboard_shortcut">_hyperscript in
Action: A Keyboard Shortcut</h3>
<p>While the counter demo is a good way to compare various approaches to
scripting, the rubber meets the road when you try to actually implement
a useful feature with an approach. For _hyperscript, let’s add a
keyboard shortcut to Contact.app: when a user hits Alt+S in our app, we
will focus the search field.</p>
<p>Since our keyboard shortcut focuses the search input, let’s put the
code for it on that search input, satisfying locality.</p>
<p>Here is the original HTML for the search input:</p>
<figure>
<div class="sourceCode" id="cb25"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb25-1"><a aria-hidden="true" href="#cb25-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Search Contacts"</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>We will add an event listener using the <code>on keydown</code>
syntax, which will fire whenever a keydown occurs. Further, we can use
an <em class="test">event filter</em> syntax in _hyperscript using square brackets
after the event. In the square brackets we can place a <em class="test">filter
expression</em> that will filter out <code>keydown</code> events we
aren’t interested in. In our case, we only want to consider events where
the Alt key is held down and where the “S” key is being pressed. We can
create a boolean expression that inspects the <code>altKey</code>
property (to see if it is <code>true</code>) and the <code>code</code>
property (to see if it is <code>"KeyS"</code>) of the event to achieve
this.</p>
<p>So far our _hyperscript looks like this:</p>
<figure>
<pre class="hyperscript"><code>on keydown[altKey and code is 'KeyS'] ...
</code></pre>
<figcaption><p>A start on our keyboard shortcut</p></figcaption>
</figure>
<p>Now, by default, _hyperscript will listen for a given event <em class="test">on
the element where it is declared</em>. So, with the script we have, we
would only get <code>keydown</code> events if the search box is already
focused. That’s not what we want! We want to have this key work
<em class="test">globally</em>, no matter which element has focus.</p>
<p>Not a problem! We can listen for the <code>keyDown</code> event
elsewhere by using a <code>from</code> clause in our event handler. In
this case we want to listen for the <code>keyDown</code> from the
window, and our code ends up looking, naturally, like this:</p>
<figure>
<pre class="hyperscript"><code>on keydown[altKey and code is 'KeyS'] from window ...
</code></pre>
<figcaption><p>Listening globally</p></figcaption>
</figure>
<p>Using the <code>from</code> clause, we can attach the listener to the
window while, at the same time, keeping the code on the element it
logically relates to.</p>
<p>Now that we’ve picked out the event we want to use to focus the
search box, let’s implement the actual focusing by calling the standard
<code>.focus()</code> method.</p>
<p>Here is the entire script, embedded in HTML:</p>
<figure>
<div class="sourceCode" id="cb28"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb28-1"><a aria-hidden="true" href="#cb28-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> id</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> name</span><span class="op">=</span><span class="st">"q"</span><span class="ot"> type</span><span class="op">=</span><span class="st">"search"</span><span class="ot"> placeholder</span><span class="op">=</span><span class="st">"Search Contacts"</span></span>
<span id="cb28-2"><a aria-hidden="true" href="#cb28-2" tabindex="-1"></a><span class="ot"> _</span><span class="op">=</span><span class="st">"on keydown[altKey and code is 'KeyS'] from the window</span></span>
<span id="cb28-3"><a aria-hidden="true" href="#cb28-3" tabindex="-1"></a><span class="st"> focus() me"</span><span class="dt">&gt;</span> <span class="er">&lt;</span>1&gt;</span></code></pre></div>
<figcaption><p>Our final script</p></figcaption>
</figure>
<ol>
<li><p>“me” refers to the element that the script is written
on.</p></li>
</ol>
<p>Given all the functionality, this is surprisingly terse, and, as an
English-like programming language, pretty easy to read.</p>
<h3 id="_why_a_new_programming_language">Why a New Programming
Language?</h3>
<p>This is all well and good, but you may be thinking “An entirely new
scripting language? That seems excessive.” And, at some level, you are
right: JavaScript is a decent scripting language, is very well optimized
and is widely understood in web development. On the other hand, by
creating an entirely new front end scripting language, we had the
freedom to address some problems that we saw generating ugly and verbose
code in JavaScript:</p>
<dl>
<dt>Async transparency</dt>
<dd>
<p>In _hyperscript, asynchronous functions (i.e., functions that return
<code>Promise</code> instances) can be invoked <em class="test">as if they were
synchronous</em>. Changing a function from sync to async does not break
any _hyperscript code that calls it. This is achieved by checking for a
Promise when evaluating any expression, and suspending the running
script if one exists (only the current event handler is suspended and
the main thread is not blocked). JavaScript, instead, requires either
the explicit use of callbacks <em class="test">or</em> the use of explicit
<code>async</code> annotations (which can’t be mixed with synchronous
code).</p>
</dd>
<dt>Array property access</dt>
<dd>
<p>In _hyperscript, accessing a property on an array (other than
<code>length</code> or a number) will return an array of the values of
property on each member of that array, making array property access act
like a flat-map operation. jQuery has a similar feature, but only for
its own data structure.</p>
</dd>
<dt>Native CSS Syntax</dt>
<dd>
<p>In _hyperscript, you can use things like CSS class and ID literals,
or CSS query literals, directly in the language, rather than needing to
call out to a wordy DOM API, as you do in JavaScript.</p>
</dd>
<dt>Deep Event Support</dt>
<dd>
<p>Working with events in _hyperscript is far more pleasant than working
with them in JavaScript, with native support for responding to and
sending events, as well as for common event-handling patterns such as
“debouncing” or rate limiting events. _hyperscript also provides
declarative mechanisms for synchronizing events within a given element
and across multiple elements.</p>
</dd>
</dl>
<p>Again we wish to stress that, in this example, we are not stepping
outside the lines of a Hypermedia-Driven Application: we are only adding
frontend, client-side functionality with our scripting. We are not
creating and managing a large amount of state outside of the DOM itself,
or communicating with the server in a non-hypermedia exchange.</p>
<p>Additionally, since _hyperscript embeds so well in HTML, it keeps the
focus <em class="test">on the hypermedia</em>, rather than on the scripting
logic.</p>
<p>It may not fit all scripting styles or needs, but _hyperscript can
provide an excellent scripting experience for Hypermedia-Driven
Applications. It is a small and obscure programming language worth a
look to understand what it is trying to achieve.</p>
<h2 id="_using_off_the_shelf_components">Using Off-the-Shelf
Components</h2>
<p>That concludes our look at three different options for <em class="test">your</em>
scripting infrastructure, that is, the code that <em class="test">you</em> write to
enhance your Hypermedia-Driven Application. However, there is another
major area to consider when discussing client side scripting: “off the
shelf” components. That is, JavaScript libraries that other people have
created that offer some sort of functionality, such as showing modal
dialogs.</p>
<p>Components have become very popular in the web development world,
with libraries like <a href="https://datatables.net/">DataTables</a>
providing rich user experiences with very little JavaScript code on the
part of a user. Unfortunately, if these libraries aren’t integrated well
into a website, they can begin to make an application feel “patched
together.” Furthermore, some libraries go beyond simple DOM
manipulation, and require that you integrate with a server endpoint,
almost invariably with a JSON data API. This means you are no longer
building a Hypermedia-Driven Application, simply because a particular
widget demands something different. A shame!</p>
<div id="sidebar">
<div>
<div>
<p><strong>Web Components</strong></p>
</div>
<div>
<p>Web Components is the collective name of a few standards; Custom
Elements and Shadow DOM, and <code>&lt;template&gt;</code> and
<code>&lt;slot&gt;</code>.</p>
<p>All of these standards bring useful capabilities to the table.
<code>&lt;template&gt;</code> elements remove their contents from the
document, while still parsing them as HTML (unlike comments) and making
them accessible to JavaScript. Custom Elements let us initialize and
tear down behaviors when elements are added or removed, which would
previously require manual work or MutationObservers. Shadow DOM lets us
encapsulate elements, leaving the “light” (non-shadow) DOM clean.</p>
<p>However, trying to reap these benefits is often frustrating. Some
difficulties are simply growing pains of new standards (like the
accessibility problems of Shadow DOM) that are actively being worked on.
Others are the result of Web Components trying to be too many things at
the same time:</p>
<ul>
<li><p>An extension mechanism for HTML. To this end, each custom element
is a tag we add to the language.</p></li>
<li><p>A lifecycle mechanism for behaviors. Methods like
<code>createdCallback</code>, <code>connectedCallback</code>, etc. allow
behavior to be added to elements without needing to be manually invoked
when those elements are added.</p></li>
<li><p>A unit of encapsulation. Shadow DOM insulates elements from their
surroundings.</p></li>
</ul>
<p>The result is that if you want any one of these things, the others
come along for the ride. If you want to attach some behaviors to some
elements using lifecycle callbacks, you need to create a new tag, which
means you can’t have multiple behaviors on one element, and you isolate
elements you add from elements already in the page, which is a problem
if they need to have ARIA relationships.</p>
<p>When should we use Web Components? A good rule of thumb is to ask
yourself: “Could this reasonably be a built-in HTML element?” For
example, a code editor is a good candidate, since HTML already has
<code>&lt;textarea&gt;</code> and <code>contenteditable</code> elements.
In addition, a fully-featured code editor will have many child elements
that won’t provide much information anyway. We can use features like <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM">Shadow
DOM</a> to encapsulate these elements<a class="footnote-ref" href="#fn2" id="fnref2" role="doc-noteref"><sup>2</sup></a>. We can create a <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements">custom
element</a>, <code>&lt;code-area&gt;</code>, that we can drop into our
page whenever we want.</p>
</div>
</div>
</div>
<h3 id="_integration_options">Integration Options</h3>
<p>The best JavaScript libraries to work with when you are building a
Hypermedia-Driven Application are ones that:</p>
<ul>
<li><p>Mutate the DOM but don’t communicate with a server over
JSON</p></li>
<li><p>Respect HTML norms (e.g., using <code>input</code> elements to
store values)</p></li>
<li><p>Trigger many custom events as the library updates things</p></li>
</ul>
<p>The last point, triggering many custom events (over the alternative
of using lots of methods and callbacks) is especially important, as
these custom events can be dispatched or listened to without additional
glue code written in a scripting language.</p>
<p>Let’s take a look at two different approaches to scripting, one using
JavaScript call backs, and one using events.</p>
<p>To make things concrete, let’s implement a better confirmation dialog
for the <code>DELETE</code> button we created in Alpine in the previous
section. In the original example we used the <code>confirm()</code>
function built in to JavaScript, which shows a pretty bare-bones system
confirmation dialog. We will replace this function with a popular
JavaScript library, SweetAlert2, that shows a much nicer looking
confirmation dialog. Unlike the <code>confirm()</code> function, which
blocks and returns a boolean (<code>true</code> if the user confirmed,
<code>false</code> otherwise), SweetAlert2 returns a
<code>Promise</code> object, which is a JavaScript mechanism for hooking
in a callback once an asynchronous action (such as waiting for a user to
confirm or deny an action) completes.</p>
<h4 id="_integrating_using_callbacks">Integrating using callbacks</h4>
<p>With SweetAlert2 installed as a library, you have access to the
<code>Swal</code> object, which has a <code>fire()</code> function on it
to trigger showing an alert. You can pass in arguments to the
<code>fire()</code> method to configure exactly what the buttons on the
confirmation dialog look like, what the title of the dialog is, and so
forth. We won’t get into these details too much, but you will see what a
dialog looks like in a bit.</p>
<p>So, given we have installed the SweetAlert2 library, we can swap it
in place of the <code>confirm()</code> function call. We then need to
restructure the code to pass a <em class="test">callback</em> to the
<code>then()</code> method on the <code>Promise</code> that
<code>Swal.fire()</code> returns. A deep dive into Promises is beyond
the scope of this chapter, but suffice to say that this callback will be
called when a user confirms or denies the action. If the user confirmed
the action, then the <code>result.isConfirmed</code> property will be
<code>true</code>.</p>
<p>Given all that, our updated code will look like this:</p>
<figure>
<div class="sourceCode" id="cb29"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb29-1"><a aria-hidden="true" href="#cb29-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">"button"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"bad bg color border"</span></span>
<span id="cb29-2"><a aria-hidden="true" href="#cb29-2" tabindex="-1"></a><span class="ot"> </span><span class="er">@</span><span class="ot">click</span><span class="op">=</span><span class="st">"Swal.fire({ </span><span class="er">&lt;</span><span class="st">1&gt;</span></span>
<span id="cb29-3"><a aria-hidden="true" href="#cb29-3" tabindex="-1"></a><span class="st"> title: 'Delete these contacts?', </span><span class="er">&lt;</span><span class="st">2&gt;</span></span>
<span id="cb29-4"><a aria-hidden="true" href="#cb29-4" tabindex="-1"></a><span class="st"> showCancelButton: true,</span></span>
<span id="cb29-5"><a aria-hidden="true" href="#cb29-5" tabindex="-1"></a><span class="st"> confirmButtonText: 'Delete'</span></span>
<span id="cb29-6"><a aria-hidden="true" href="#cb29-6" tabindex="-1"></a><span class="st"> }).then((result) =&gt; { </span><span class="er">&lt;</span><span class="st">3&gt;</span></span>
<span id="cb29-7"><a aria-hidden="true" href="#cb29-7" tabindex="-1"></a><span class="st"> if (result.isConfirmed) htmx.ajax('DELETE', '/contacts',</span></span>
<span id="cb29-8"><a aria-hidden="true" href="#cb29-8" tabindex="-1"></a><span class="st"> { source: $root, target: document.body })</span></span>
<span id="cb29-9"><a aria-hidden="true" href="#cb29-9" tabindex="-1"></a><span class="st"> });"</span></span>
<span id="cb29-10"><a aria-hidden="true" href="#cb29-10" tabindex="-1"></a><span class="dt">&gt;</span>Delete<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span></code></pre></div>
<figcaption><p>A callback-based confirmation dialog</p></figcaption>
</figure>
<ol>
<li><p>Invoke the <code>Swal.fire()</code> function</p></li>
<li><p>Configure the dialog</p></li>
<li><p>Handle the result of the user’s selection</p></li>
</ol>
<p>And now, when this button is clicked, we get a nice looking dialog in
our web application (<a class="ref" href="#fig-swal-screenshot">[fig-swal-screenshot]</a>) — much nicer than the system
confirmation dialog. Still, this feels a little wrong. This is a lot of
code to write just to trigger a slightly nicer <code>confirm()</code>,
isn’t it? And the htmx JavaScript code we are using here feels awkward.
It would be more natural to move the htmx out to attributes on the
button, as we have been doing, and then trigger the request via
events.</p>
<figure id="fig-swal-screenshot">
<p><img src="https://hypermedia.systems/images/screenshot_sweet_alert.png"/></p>
<figcaption><p>A SweetAlert dialog box</p></figcaption>
</figure>
<p>So let’s take a different approach and see how that looks.</p>
<h4 id="_integrating_using_events">Integrating using events</h4>
<p>To clean this code up, we will pull the <code>Swal.fire()</code> code
out to a custom JavaScript function we will create called
<code>sweetConfirm()</code>. <code>sweetConfirm()</code> will take the
dialog options that are passed into the <code>fire()</code> method, as
well as the element that is confirming an action. The big difference
here is that the new <code>sweetConfirm()</code> function, rather than
calling some htmx directly, will instead trigger a
<code>confirmed</code> event on the button when the user confirms they
wish to delete.</p>
<p>Here is what our JavaScript function looks like:</p>
<figure>
<div class="sourceCode" id="cb30"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb30-1"><a aria-hidden="true" href="#cb30-1" tabindex="-1"></a><span class="kw">function</span> <span class="fu">sweetConfirm</span>(elt<span class="op">,</span> config) {</span>
<span id="cb30-2"><a aria-hidden="true" href="#cb30-2" tabindex="-1"></a> Swal<span class="op">.</span><span class="fu">fire</span>(config) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb30-3"><a aria-hidden="true" href="#cb30-3" tabindex="-1"></a> <span class="op">.</span><span class="fu">then</span>((result) <span class="kw">=&gt;</span> {</span>
<span id="cb30-4"><a aria-hidden="true" href="#cb30-4" tabindex="-1"></a> <span class="cf">if</span> (result<span class="op">.</span><span class="at">isConfirmed</span>) {</span>
<span id="cb30-5"><a aria-hidden="true" href="#cb30-5" tabindex="-1"></a> elt<span class="op">.</span><span class="fu">dispatchEvent</span>(<span class="kw">new</span> <span class="bu">Event</span>(<span class="st">'confirmed'</span>))<span class="op">;</span> <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb30-6"><a aria-hidden="true" href="#cb30-6" tabindex="-1"></a> }</span>
<span id="cb30-7"><a aria-hidden="true" href="#cb30-7" tabindex="-1"></a> })<span class="op">;</span></span>
<span id="cb30-8"><a aria-hidden="true" href="#cb30-8" tabindex="-1"></a>}</span></code></pre></div>
<figcaption><p>An event-based confirmation dialog</p></figcaption>
</figure>
<ol>
<li><p>Pass the config through to the <code>fire()</code>
function.</p></li>
<li><p>If the user confirmed the action, trigger a
<code>confirmed</code> event.</p></li>
</ol>
<p>With this method available, we can now tighten up our delete button
quite a bit. We can remove all the SweetAlert2 code that we had in the
<code>@click</code> Alpine attribute, and simply call this new
<code>sweetConfirm()</code> method, passing in the arguments
<code>$el</code>, which is the Alpine syntax for getting “the current
element” that the script is on, and then the exact configuration we want
for our dialog.</p>
<p>If the user confirms the action, a <code>confirmed</code> event will
be triggered on the button. This means that we can go back to using our
trusty htmx attributes! Namely, we can move <code>DELETE</code> to an
<code>hx-delete</code> attribute, and we can use <code>hx-target</code>
to target the body. And then, and here is the crucial step, we can use
the <code>confirmed</code> event that is triggered in the
<code>sweetConfirm()</code> function, to trigger the request, but adding
an <code>hx-trigger</code> for it.</p>
<p>Here is what our code looks like:</p>
<figure>
<div class="sourceCode" id="cb31"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb31-1"><a aria-hidden="true" href="#cb31-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">"button"</span><span class="ot"> class</span><span class="op">=</span><span class="st">"bad bg color border"</span></span>
<span id="cb31-2"><a aria-hidden="true" href="#cb31-2" tabindex="-1"></a><span class="ot"> hx-delete</span><span class="op">=</span><span class="st">"/contacts"</span><span class="ot"> hx-target</span><span class="op">=</span><span class="st">"body"</span><span class="ot"> hx-trigger</span><span class="op">=</span><span class="st">"confirmed"</span><span class="ot"> </span><span class="er">&lt;1</span><span class="dt">&gt;</span></span>
<span id="cb31-3"><a aria-hidden="true" href="#cb31-3" tabindex="-1"></a> @click="sweetConfirm($el, { <span class="er">&lt;</span>2&gt;</span>
<span id="cb31-4"><a aria-hidden="true" href="#cb31-4" tabindex="-1"></a> title: 'Delete these contacts?', <span class="er">&lt;</span>3&gt;</span>
<span id="cb31-5"><a aria-hidden="true" href="#cb31-5" tabindex="-1"></a> showCancelButton: true,</span>
<span id="cb31-6"><a aria-hidden="true" href="#cb31-6" tabindex="-1"></a> confirmButtonText: 'Delete'</span>
<span id="cb31-7"><a aria-hidden="true" href="#cb31-7" tabindex="-1"></a> })"&gt;</span></code></pre></div>
<figcaption><p>An Event-based Confirmation Dialog</p></figcaption>
</figure>
<ol>
<li><p>Our htmx attributes are back.</p></li>
<li><p>We pass the button in to the function, so an event can be
triggered on it.</p></li>
<li><p>We pass through the SweetAlert2 configuration
information.</p></li>
</ol>
<p>As you can see, this event-based code is much cleaner and certainly
more “HTML-ish.” The key to this cleaner implementation is that our new
<code>sweetConfirm()</code> function fires an event that htmx is able to
listen for.</p>
<p>This is why a rich event model is important to look for when choosing
a library to work with, both with htmx and with Hypermedia-Driven
Applications in general.</p>
<p>Unfortunately, due to the prevalence and dominance of the
JavaScript-first mindset today, many libraries are like SweetAlert2:
they expect you to pass a callback in the first style. In these cases
you can use the technique we have demonstrated here, wrapping the
library in a function that triggers events in a callback, to make the
library more hypermedia and htmx-friendly.</p>
<h2 id="_pragmatic_scripting">Pragmatic Scripting</h2>
<blockquote>
<p>In case of conflict, consider users over authors over implementors
over specifiers over theoretical purity.</p>
</blockquote><p class="quote-attribution"> W3C, HTML Design Principles § 3.2 Priority of Constituencies</p>
<p>We have looked at several tools and techniques for scripting in a
Hypermedia-Driven Application. How should you pick between them? The sad
truth is that there will never be a single, always correct answer to
this question.</p>
<p>Are you committed to vanilla JavaScript-only, perhaps due to company
policy? Well, you can use vanilla JavaScript effectively to script your
Hypermedia-Driven Application.</p>
<p>Do you have more leeway and like the look of Alpine.js? That’s a
great way to add more structured, localized JavaScript to your
application, and offers some nice reactive features as well.</p>
<p>Are you a bit more bold in your technical choices? Maybe _hyperscript
is worth a look. (We certainly think so.)</p>
<p>Sometimes you might even consider picking two (or more) of these
approaches within an application. Each has its own strengths and
weaknesses, and all of them are relatively small and self-contained, so
picking the right tool for the job at hand might be the best
approach.</p>
<p>In general, we encourage a <em class="test">pragmatic</em> approach to scripting:
whatever feels right is probably right (or, at least, right
<em class="test">enough</em>) for you. Rather than being concerned about which
particular approach is taken for your scripting, we would focus on these
more general concerns:</p>
<ul>
<li><p>Avoid communicating with the server via JSON data APIs.</p></li>
<li><p>Avoid storing large amounts of state outside of the DOM.</p></li>
<li><p>Favor using events, rather than hard-coded callbacks or method
calls.</p></li>
</ul>
<p>And even on these topics, sometimes a web developer has to do what a
web developer has to do. If the perfect widget for your application
exists but uses a JSON data API? That’s OK.</p>
<p>Just don’t make it a habit.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: HTML is for Applications</h2>
<p>A prevalent meme among developers suggests that HTML was designed for
“documents” and is unsuitable for “applications.” In reality, hypermedia
is not only a sophisticated, modern architecture for applications, but
it can allow us to do away with this artificial app/document split for
good.</p>
<blockquote>
<p>When I say Hypertext, I mean the simultaneous presentation of
information and controls such that the information becomes the
affordance through which the user obtains choices and selects
actions.</p>
</blockquote><p class="quote-attribution"> Roy Fielding, <a href="https://www.slideshare.net/royfielding/a-little-rest-and-relaxation">A
little REST and Relaxation</a></p>
<p>HTML allows documents to contain rich multimedia including images,
audio, video, JavaScript programs, vector graphics and (with some help)
3D environments. More importantly, however, it allows interactive
controls to be embedded within these documents, allowing the information
itself to be the app through which it is accessed.</p>
<p>Consider: Is it not mind-boggling that a single application — which
works on all types of computers and OSs — can let you read news, place
video calls, compose documents, enter virtual worlds, and do almost any
other everyday computing task?</p>
<p>Unfortunately, it is the interactive capabilities of HTML that is its
least developed aspect. For reasons unknown to us, while HTML made it to
version 5 and became a Living Standard, accreting many game-changing
features on the way, the data interactions in it are still mainly
restricted to links and forms. It’s up to developers to extend HTML, and
we want to do so in a way that doesn’t abstract over its simplicity with
an imitation of classical “native” toolkits.</p>
<blockquote>
<ul>
<li><p><span class="smallcaps">Software was not supposed to use native
toolkits</span></p></li>
<li><p><span class="smallcaps">Years of windows UI libraries</span> yet
<span class="smallcaps">no real-world use found</span> for going lower
level than <span class="smallcaps">the Web</span></p></li>
<li><p>Wanted a window anyway for a laugh? We had a tool for that: It
was called “<span class="smallcaps">Electron</span>”</p></li>
<li><p>“yes I would love to write 4 <span class="smallcaps">different</span> copies of the same UI” - Statements
dreamed up by the Utterly Deranged</p></li>
</ul>
</blockquote><p class="quote-attribution"> Leah Clark, @leah@tilde.zone</p>
</div>
</div>
<section class="footnotes footnotes-end-of-document" id="footnotes" role="doc-endnotes">
<hr/>
<ol>
<li id="fn1"><p>Rendering here refers to HTML generation. Framework
support for server-side rendering is not needed in a HDA because
generating HTML on the server is the default.<a class="footnote-back" href="#fnref1" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>Beware that Shadow DOM is a newer web platform feature
that’s still in development at the time of writing. In particular, there
are some accessibility bugs that may occur when elements inside and
outside the shadow root interact.<a class="footnote-back" href="#fnref2" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">JSON Data APIs</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/json-data-apis/#_hypermedia_apis_json_data_apis">Hypermedia APIs &amp; JSON Data
APIs</a>
<ul>
<li>
<a href="https://hypermedia.systems/json-data-apis/#_differences_between_hypermedia_apis_data_apis">Differences
Between Hypermedia APIs &amp; Data APIs</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#_adding_a_json_data_api_to_contact_app">Adding a JSON Data API
To Contact.app</a>
<ul>
<li>
<a href="https://hypermedia.systems/json-data-apis/#_picking_a_root_url_for_our_api">Picking a Root URL For Our
API</a>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#our-first-json-endpoint--listing-all-contacts">Our First JSON Endpoint: Listing All Contacts</a>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#adding-contacts">Adding Contacts</a>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#_viewing_contact_details">Viewing Contact Details</a>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#_updating_deleting_contacts">Updating &amp; Deleting
Contacts</a>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#_additional_data_api_considerations">Additional Data API
Considerations</a>
<ul>
<li>
<a href="https://hypermedia.systems/json-data-apis/#authentication-in-web-applications">Authentication in web applications</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#_the_shape_of_our_two_apis">The “Shape” of Our Two APIs</a>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#_the_model_view_controller_mvc_paradigm">The Model View
Controller (MVC) Paradigm</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/json-data-apis/#html-note-title">HTML Notes: Microformats</a>
</li></ul>
</details>
<div class="division-content">
<p>So far we have been focusing on using hypermedia to build
Hypermedia-Driven Applications. In doing so we are following and taking
advantage of the native network architecture of the web, and building a
RESTful system, in the original sense of that term.</p>
<p>However, today, we should acknowledge that many web applications are
often <em class="test">not</em> built using this approach. Instead, they use a Single
Page Application front end library such as React to build their
application, and they interact with the server via a JSON API. This JSON
API almost never uses hypermedia concepts. Rather JSON APIs tend to be
<em class="test">Data APIs</em>, that is, an API that simply returns structured
domain data to the client without any hypermedia control information.
The client itself must know how to interpret the JSON Data: what end
points are associated with the data, how certain fields should be
interpreted, and so on.</p>
<p>Now, believe it or not, we <em class="test">have</em> been creating an API for
Contact.app.</p>
<p>This may sound confusing to you: an API? We have just been creating a
web application, with handlers that just return HTML.</p>
<p>How is that an API?</p>
<p>It turns out that Contact.app is, indeed, providing an API. It just
happens to be a <em class="test">hypermedia</em> API that a <em class="test">hypermedia
client</em>, that is, a browser, understands. We are building an API for
the browser to interact with over HTTP, and, thanks to the magic of HTML
and hypermedia, the browser doesn’t need to know anything about our
hypermedia API beyond an entry point URL: all the actions and display
information comes, self-contained, within the HTML responses.</p>
<p>Building RESTful web applications like this is so natural and simple
that you might not think of it as an API at all, but we assure you, it
is.</p>
<h2 id="_hypermedia_apis_json_data_apis">Hypermedia APIs &amp; JSON Data
APIs</h2>
<p>So, we have a hypermedia API for Contact.app. Should we include a
Data API for Contact.app as well?</p>
<p>Sure! The existence of a hypermedia API <em class="test">in no way means</em> that
you can’t <em class="test">also</em> have a Data API. In fact, this is a common
situation in traditional web applications: there is the “web
application” that is entered through that entry point URL, say
<code>https://mywebapp.example.com/</code>. And there is also a
<em class="test">separate</em> JSON API that is accessible through another URL,
perhaps <code>https://api.mywebapp.example.com/v1</code>.</p>
<p>This is a perfectly reasonable way to split up the hypermedia
interface to your application and the Data API you provide to other,
non-hypermedia clients.</p>
<p>Why would you want to include a Data API along with a hypermedia API?
Well, because <em class="test">non-hypermedia clients</em> might also want to
interact with your application as well.</p>
<p>For example:</p>
<ul>
<li><p>Perhaps you have a mobile application that isn’t built using
Hyperview. That application will need to interact with your server
somehow, and using the existing HTML API would almost certainly be a
poor fit! You want programmatic access to your system via a Data API,
and JSON is a natural choice for this.</p></li>
<li><p>Perhaps you have an automated script that needs to interact with
the system on a regular basis. For example, maybe we have a bulk-import
job that runs nightly, and needs to import/sync thousands of contacts.
While it would be possible to script this against the HTML API, it would
also be annoying: parsing HTML in scripts is error prone and tedious. It
would be better to have a simple JSON API for this use case.</p></li>
<li><p>Perhaps there are 3rd party clients who wish to integrate with
your system’s data in some way. Maybe a partner wants to synchronize
data nightly. As with the bulk-import example, this isn’t a great use
case for an HTML-based API, and it would make more sense to provide
something more amenable to scripting.</p></li>
</ul>
<p>For all of these use cases, a JSON Data API makes sense: in each case
the API is not being consumed by a hypermedia client, so presenting an
HTML-based hypermedia API would be inefficient and complicated for the
client to deal with. A simple JSON Data API fits the bill for what we
want and, as always, we recommend using the right tool for the job.</p>
<div id="sidebar">
<div>
<div>
<p><strong>“What!?! You Want Me To Parse HTML!?!”</strong></p>
</div>
<div>
<p>A confusion we often run into in online discussions when we advocate
a hypermedia approach to building web applications is that people think
we mean that they should parse the HTML responses from the server, and
then dump the data into their SPA framework or mobile applications.</p>
<p>This is, of course, silly.</p>
<p>What we mean, instead, is that you should consider using a hypermedia
API <em class="test">with a hypermedia client</em>, like the browser, interpreting
the hypermedia response itself and presenting it to the user. A
hypermedia API can’t simply be welded on top of an existing SPA
approach. It requires a sophisticated hypermedia client such as the
browser to be consumed effectively.</p>
<p>If you are writing code to tease apart your hypermedia only to then
treat as data to feed into a client-side model, you are probably doing
it wrong.</p>
</div>
</div>
</div>
<h3 id="_differences_between_hypermedia_apis_data_apis">Differences
Between Hypermedia APIs &amp; Data APIs</h3>
<p>Let’s accept for a moment that we <em class="test">are</em> going to have a Data
API for our application, in addition to our hypermedia API. At this
point, some developers may be wondering: why have both? Why not have a
single API, the JSON Data API, and have multiple clients use this one
API to communicate with it?</p>
<p>Isn’t it redundant to have both types of APIs for our
application?</p>
<p>This is a reasonable point: we do advocate having multiple APIs to
your web application if necessary and, yes, this may lead to some
redundancy in code. However, there are distinct advantages to both sorts
of APIs and, even more so, distinct requirements for both sorts of
APIs.</p>
<p>By supporting both of these types of APIs separately you can get the
strengths of both, while keeping their varying styles of code and
infrastructure needs cleanly split out.</p>
<p>Let’s contrast the needs of JSON APIs with Hypermedia APIs:</p>
<div data-align="center">
<table>
<tbody>
<tr class="odd">
<td><p>JSON API Needs</p></td>
<td><p>Hypermedia API</p></td>
</tr>
<tr class="even">
<td><p>It must remain stable over time: you cannot change the API
willy-nilly or you risk breaking clients that use the API and expect
certain end points to behave in certain ways.</p></td>
<td><p>There is no need to remain stable over time: all URLs are
discovered via HTML responses, so you can be much more aggressive in
changing the shape of a hypermedia API.</p></td>
</tr>
<tr class="odd">
<td><p>It must be versioned: related to the first point, when you do
make a major change, you need to version the API so that clients that
are using the old API continue to work.</p></td>
<td><p>Versioning is not an issue, another strength of the hypermedia
approach.</p></td>
</tr>
<tr class="even">
<td><p>It should be rate limited: since data APIs are often used by
other clients, not just your own internal web application, requests
should be rate limited, often by user, in order to avoid a single client
overloading the system.</p></td>
<td><p>Rate limiting probably isn’t as important beyond the prevention
of Distributed Denial of Service (DDoS) attacks.</p></td>
</tr>
<tr class="odd">
<td><p>It should be a general API: since the API is for <em class="test">all</em>
clients, not just for your web application, you should avoid specialized
end points that are driven by your own application needs. Instead, the
API should be general and expressive enough to satisfy as many potential
client needs as possible.</p></td>
<td><p>The API can be <em class="test">very specific</em> to your application needs:
since it is designed only for your particular web application, and since
the API is discovered through hypermedia, you can add and remove highly
tuned end points for specific features or optimization needs in your
application.</p></td>
</tr>
<tr class="even">
<td><p>Authentication for these sorts of API is typically token based,
which we will discuss in more detail later.</p></td>
<td><p>Authentication is typically managed through a session cookie
established by a login page.</p></td>
</tr>
</tbody>
</table>
</div>
<p>These two different types of APIs have different strengths and needs,
so it makes sense to use both. The hypermedia approach can be used for
your web application, allowing you to specialize the API for the “shape”
of your application. The Data API approach can be used for other,
non-hypermedia clients like mobile, integration partners, etc.</p>
<p>Note that by splitting these two APIs apart, you reduce the pressure
to constantly change a general Data API to address application needs.
Your Data API can focus on remaining stable and reliable, rather than
requiring a new version with every added feature.</p>
<p>This is the key advantage of splitting your Data API from your
Hypermedia API.</p>
<div id="sidebar">
<div>
<div>
<p><strong>JSON Data APIs vs JSON “REST” APIs</strong></p>
</div>
<div>
<p>Unfortunately, today, for historical reasons, what we are calling
JSON Data APIs are often referred to as “REST APIs” in the industry.
This is ironic, because, by any reasonable reading of Roy Fielding’s
work defining what REST means, the vast majority of JSON APIs are
<em class="test">not</em> RESTful. Not even close.</p>
<blockquote>
<p>I am getting frustrated by the number of people calling any
HTTP-based interface a REST API. Today’s example is the SocialSite REST
API. That is RPC. It screams RPC. There is so much coupling on display
that it should be given an X rating.</p>
<p>What needs to be done to make the REST architectural style clear on
the notion that hypertext is a constraint? In other words, if the engine
of application state (and hence the API) is not being driven by
hypertext, then it cannot be RESTful and cannot be a REST API. Period.
Is there some broken manual somewhere that needs to be fixed?</p>
</blockquote><p class="quote-attribution"> Roy Fielding,
https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven</p>
<p>The story of how “REST API” came to mean “JSON APIs” in the industry
is a long and sordid one, and beyond the scope of this book. However, if
you are interested, you can refer to an essay by one of the authors of
this book entitled “How Did REST Come To Mean The Opposite of REST?” on
the <a href="https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/">htmx
website</a>.</p>
<p>In this book we use the term “Data API” to describe these JSON APIs,
while acknowledging that many people in the industry will continue to
call them “REST APIs” for the foreseeable future.</p>
</div>
</div>
</div>
<h2 id="_adding_a_json_data_api_to_contact_app">Adding a JSON Data API
To Contact.app</h2>
<p>Alright, so how are we going to add a JSON Data API to our
application? One approach, popularized by the Ruby on Rails web
framework, is to use the same URL endpoints as your hypermedia
application, but use the HTTP <code>Accept</code> header to determine if
the client wants a JSON representation or an HTML representation. The
HTTP <code>Accept</code> header allows a client to specify what sort of
Multipurpose Internet Mail Extensions (MIME) types, that is file types,
it wants back from the server: JSON, HTML, text and so on.</p>
<p>So, if the client wanted a JSON representation of all contacts, they
might issue a <code>GET</code> request that looks like this:</p>
<figure>
<pre class="http"><code>Accept: application/json
GET /contacts
</code></pre>
<figcaption><p>A request for a JSON representation of all
contacts</p></figcaption>
</figure>
<p>If we adopted this pattern then our request handler for
<code>/contacts/</code> would need to be updated to inspect this header
and, depending on the value, return a JSON rather than HTML
representation for the contacts. Ruby on Rails has support for this
pattern baked into the framework, making it very easy to switch on the
requested MIME type.</p>
<p>Unfortunately, our experience with this pattern has not been great,
for reasons that should be clear given the differences we outlined
between Data and hypermedia APIs: they have different needs and often
take on very different “shapes”, and trying to pound them into the same
set of URLs ends up creating a lot of tension in the application
code.</p>
<p>Given the different needs of the two APIs and our experience managing
multiple APIs like this, we think separating the two, and, therefore,
breaking the JSON Data API out to its own set of URLs is the right
choice. This will allow us to evolve the two APIs separately from one
another, and give us room to improve each independently, in a manner
consistent with their own individual strengths.</p>
<h3 id="_picking_a_root_url_for_our_api">Picking a Root URL For Our
API</h3>
<p>Given that we are going to split our JSON Data API routes out from
our regular hypermedia routes, where should we place them? One important
consideration here is that we want to make sure that we can version our
API cleanly in some way, regardless of the pattern we choose.</p>
<p>Looking around, a lot of places use a subdomain for their APIs,
something like <code>https://api.mywebapp.example.com</code> and, in
fact, often encode versioning in the subdomain:
<code>https://v1.api.mywebapp.example.com</code>.</p>
<p>While this makes sense for large companies, it seems like a bit of
overkill for our modest little Contact.app. Rather than using
subdomains, which are a pain for local development, we will use
sub-paths within the existing application:</p>
<ul>
<li><p>We will use <code>/api</code> as the root for our Data API
functionality</p></li>
<li><p>We will use <code>/api/v1</code> as the entry point for version 1
of our Data API</p></li>
</ul>
<p>If and when we decide to bump the API version, we can move to
<code>/api/v2</code> and so on.</p>
<p>This approach isn’t perfect, of course, but it will work for our
simple application and can be adapted to a subdomain approach or various
other methods at a later point, when our Contact.app has taken over the
internet and we can afford a large team of API developers. :)</p>
<h3 id="our-first-json-endpoint--listing-all-contacts">Our First JSON Endpoint: Listing All Contacts</h3>
<p>Let’s add our first Data API endpoint. It will handle an HTTP
<code>GET</code> request to <code>/api/v1/contacts</code>, and return a
JSON list of all contacts in the system. In some ways it will look quite
a bit like our initial code for the hypermedia route
<code>/contacts</code>: we will load all the contacts from the contacts
database and then render some text as a response.</p>
<p>We are also going to take advantage of a nice feature of Flask: if
you simply return an object from a handler, it will serialize (that is,
convert) that object into a JSON response. This makes it very easy to
build simple JSON APIs in flask!</p>
<figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a aria-hidden="true" href="#cb2-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/api/v1/contacts"</span>, methods<span class="op">=</span>[<span class="st">"GET"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb2-2"><a aria-hidden="true" href="#cb2-2" tabindex="-1"></a><span class="kw">def</span> json_contacts():</span>
<span id="cb2-3"><a aria-hidden="true" href="#cb2-3" tabindex="-1"></a> contacts_set <span class="op">=</span> Contact.<span class="bu">all</span>()</span>
<span id="cb2-4"><a aria-hidden="true" href="#cb2-4" tabindex="-1"></a> contacts_dicts <span class="op">=</span> [c.__dict__ <span class="cf">for</span> c <span class="kw">in</span> contacts_set] <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb2-5"><a aria-hidden="true" href="#cb2-5" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">"contacts"</span>: contacts_dicts} <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>A JSON data API to return all contacts</p></figcaption>
</figure>
<ol>
<li><p>JSON API gets its own path, starting with
<code>/api</code>.</p></li>
<li><p>Convert the contacts array into an array of simple dictionary
(map) objects.</p></li>
<li><p>Return a dictionary that contains a <code>contacts</code>
property of all the contacts.</p></li>
</ol>
<p>This Python code might look a little foreign to you if you are not a
Python developer, but all we are doing is converting our contacts into
an array of simple name/value pairs and returning that array in an
enclosing object as the <code>contacts</code> property. This object will
be serialized into a JSON response automatically by Flask.</p>
<p>With this in place, if we make an HTTP <code>GET</code> request to
<code>/api/v1/contacts</code>, we will see a response that looks
something like this:</p>
<figure>
<div class="sourceCode" id="cb3"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb3-1"><a aria-hidden="true" href="#cb3-1" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb3-2"><a aria-hidden="true" href="#cb3-2" tabindex="-1"></a> <span class="dt">"contacts"</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb3-3"><a aria-hidden="true" href="#cb3-3" tabindex="-1"></a> <span class="fu">{</span></span>
<span id="cb3-4"><a aria-hidden="true" href="#cb3-4" tabindex="-1"></a> <span class="dt">"email"</span><span class="fu">:</span> <span class="st">"carson@example.com"</span><span class="fu">,</span></span>
<span id="cb3-5"><a aria-hidden="true" href="#cb3-5" tabindex="-1"></a> <span class="dt">"errors"</span><span class="fu">:</span> <span class="fu">{},</span></span>
<span id="cb3-6"><a aria-hidden="true" href="#cb3-6" tabindex="-1"></a> <span class="dt">"first"</span><span class="fu">:</span> <span class="st">"Carson"</span><span class="fu">,</span></span>
<span id="cb3-7"><a aria-hidden="true" href="#cb3-7" tabindex="-1"></a> <span class="dt">"id"</span><span class="fu">:</span> <span class="dv">2</span><span class="fu">,</span></span>
<span id="cb3-8"><a aria-hidden="true" href="#cb3-8" tabindex="-1"></a> <span class="dt">"last"</span><span class="fu">:</span> <span class="st">"Gross"</span><span class="fu">,</span></span>
<span id="cb3-9"><a aria-hidden="true" href="#cb3-9" tabindex="-1"></a> <span class="dt">"phone"</span><span class="fu">:</span> <span class="st">"123-456-7890"</span></span>
<span id="cb3-10"><a aria-hidden="true" href="#cb3-10" tabindex="-1"></a> <span class="fu">}</span><span class="ot">,</span></span>
<span id="cb3-11"><a aria-hidden="true" href="#cb3-11" tabindex="-1"></a> <span class="fu">{</span></span>
<span id="cb3-12"><a aria-hidden="true" href="#cb3-12" tabindex="-1"></a> <span class="dt">"email"</span><span class="fu">:</span> <span class="st">"joe@example2.com"</span><span class="fu">,</span></span>
<span id="cb3-13"><a aria-hidden="true" href="#cb3-13" tabindex="-1"></a> <span class="dt">"errors"</span><span class="fu">:</span> <span class="fu">{},</span></span>
<span id="cb3-14"><a aria-hidden="true" href="#cb3-14" tabindex="-1"></a> <span class="dt">"first"</span><span class="fu">:</span> <span class="st">""</span><span class="fu">,</span></span>
<span id="cb3-15"><a aria-hidden="true" href="#cb3-15" tabindex="-1"></a> <span class="dt">"id"</span><span class="fu">:</span> <span class="dv">3</span><span class="fu">,</span></span>
<span id="cb3-16"><a aria-hidden="true" href="#cb3-16" tabindex="-1"></a> <span class="dt">"last"</span><span class="fu">:</span> <span class="st">""</span><span class="fu">,</span></span>
<span id="cb3-17"><a aria-hidden="true" href="#cb3-17" tabindex="-1"></a> <span class="dt">"phone"</span><span class="fu">:</span> <span class="st">""</span></span>
<span id="cb3-18"><a aria-hidden="true" href="#cb3-18" tabindex="-1"></a> <span class="fu">}</span><span class="ot">,</span></span>
<span id="cb3-19"><a aria-hidden="true" href="#cb3-19" tabindex="-1"></a> <span class="er">...</span></span>
<span id="cb3-20"><a aria-hidden="true" href="#cb3-20" tabindex="-1"></a> <span class="ot">]</span></span>
<span id="cb3-21"><a aria-hidden="true" href="#cb3-21" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<figcaption><p>Some sample data from our API</p></figcaption>
</figure>
<p>So, you can see, we now have a way to get a relatively simple JSON
representation of our contacts via an HTTP request. Not perfect, but
it’s a good start. It’s certainly good enough to write some basic
automated scripts against. For example, you could use this Data API
to:</p>
<ul>
<li><p>Move your contacts to another system on a nightly basis</p></li>
<li><p>Back your contacts up to a local file</p></li>
<li><p>Automate an email blast to your contacts</p></li>
</ul>
<p>Having this small JSON Data API opens up a lot of automation
possibilities that would be messier to achieve with our existing
hypermedia API.</p>
<h3 id="adding-contacts">Adding Contacts</h3>
<p>Let’s move on to the next piece of functionality: the ability to add
a new contact. Once again, our code is going to look similar in some
ways to the code that we wrote for our normal web application. However,
here we are also going to see the JSON API and the hypermedia API for
our web application begin to obviously diverge.</p>
<p>In the web application, we needed a separate path,
<code>/contacts/new</code> to host the HTML form for creating a new
contact. In the web application we made the decision to issue a
<code>POST</code> to that same path to keep things consistent.</p>
<p>In the case of the JSON API, there is no such path needed: the JSON
API “just is”: it doesn’t need to provide any hypermedia representation
for creating a new contact. You simply know where to issue a
<code>POST</code> to create a contact — likely through some
documentation provided about the API — and that’s it.</p>
<p>Because of that fact, we can put the “create” handler on the same
path as the “list” handler: <code>/api/v1/contacts</code>, but have it
respond only to HTTP <code>POST</code> requests.</p>
<p>The code here is relatively straightforward: populate a new contact
with the information from the <code>POST</code> request, attempt to save
it, and — if it is not successful — show error messages. Here is the
code:</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/api/v1/contacts"</span>, methods<span class="op">=</span>[<span class="st">"POST"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a><span class="kw">def</span> json_contacts_new():</span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a> c <span class="op">=</span> Contact(<span class="va">None</span>,</span>
<span id="cb4-4"><a aria-hidden="true" href="#cb4-4" tabindex="-1"></a> request.form.get(<span class="st">'first_name'</span>),</span>
<span id="cb4-5"><a aria-hidden="true" href="#cb4-5" tabindex="-1"></a> request.form.get(<span class="st">'last_name'</span>),</span>
<span id="cb4-6"><a aria-hidden="true" href="#cb4-6" tabindex="-1"></a> request.form.get(<span class="st">'phone'</span>),</span>
<span id="cb4-7"><a aria-hidden="true" href="#cb4-7" tabindex="-1"></a> request.form.get(<span class="st">'email'</span>)) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb4-8"><a aria-hidden="true" href="#cb4-8" tabindex="-1"></a> <span class="cf">if</span> c.save(): <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb4-9"><a aria-hidden="true" href="#cb4-9" tabindex="-1"></a> <span class="cf">return</span> c.__dict__</span>
<span id="cb4-10"><a aria-hidden="true" href="#cb4-10" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb4-11"><a aria-hidden="true" href="#cb4-11" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">"errors"</span>: c.errors}, <span class="dv">400</span> <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Adding contacts with our JSON API</p></figcaption>
</figure>
<ol>
<li><p>This handler is on the same path as the first one for our JSON
API, but handles <code>POST</code> requests.</p></li>
<li><p>We create a new Contact based on values submitted with the
request.</p></li>
<li><p>We attempt to save the contact and, if successful, render it as a
JSON object.</p></li>
<li><p>If the save is not successful, we render an object showing the
errors, with a response code of <code>400 (Bad Request)</code>.</p></li>
</ol>
<p>In some ways this is similar to our <code>contacts_new()</code>
handler from our web application; we are creating the contact and
attempting to save it. In other ways it is very different:</p>
<ul>
<li><p>There is no redirection happening here on a successful creation,
because we are not dealing with a hypermedia client like the
browser.</p></li>
<li><p>In the case of a bad request, we simply return an error response
code, <code>400 (Bad Request)</code>. This is in contrast with the web
application, where we re-render the form with error messages in
it.</p></li>
</ul>
<p>These sorts of differences, over time, build up and make the idea of
keeping your JSON and hypermedia APIs on the same set of URLs less and
less appealing.</p>
<h3 id="_viewing_contact_details">Viewing Contact Details</h3>
<p>Next, let’s make it possible for a JSON API client to download the
details for a single contact. We will naturally use an HTTP
<code>GET</code> for this functionality and will follow the convention
we established for our regular web application, and put the path at
<code>/api/v1/contacts/&lt;contact id&gt;</code>. So, for example, if
you want to see the details of the contact with the id <code>42</code>,
you would issue an HTTP <code>GET</code> to
<code>/api/v1/contacts/42</code>.</p>
<p>This code is quite simple:</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/api/v1/contacts/&lt;contact_id&gt;"</span>, methods<span class="op">=</span>[<span class="st">"GET"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a><span class="kw">def</span> json_contacts_view(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> <span class="cf">return</span> contact.__dict__ <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Getting the details of a contact in JSON</p></figcaption>
</figure>
<ol>
<li><p>Add a new <code>GET</code> route at the path we want to use for
viewing contact details</p></li>
<li><p>Look the contact up via the id passed in through the
path</p></li>
<li><p>Convert the contact to a dictionary, so it can be rendered as
JSON response</p></li>
</ol>
<p>Nothing too complicated: we look the contact up by the ID provided in
the path to the controller. We then render it as JSON. You have to
appreciate the simplicity of this code!</p>
<p>Next, let’s add updating and deleting a contact as well.</p>
<h3 id="_updating_deleting_contacts">Updating &amp; Deleting
Contacts</h3>
<p>As with the create contact API endpoint, because there is no HTML UI
to produce for them, we can reuse the
<code>/api/v1/contacts/&lt;contact id&gt;</code> path. We will use the
<code>PUT</code> HTTP method for updating a contact and the
<code>DELETE</code> method for deleting one.</p>
<p>Our update code is going to look nearly identical to the create
handler, except that, rather than creating a new contact, we will look
up the contact by ID and update its fields. In this sense we are just
combining the code of the create handler and the detail view
handler.</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/api/v1/contacts/&lt;contact_id&gt;"</span>, methods<span class="op">=</span>[<span class="st">"PUT"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a><span class="kw">def</span> json_contacts_edit(contact_id):</span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a> c <span class="op">=</span> Contact.find(contact_id) <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a> c.update(</span>
<span id="cb6-5"><a aria-hidden="true" href="#cb6-5" tabindex="-1"></a> request.form[<span class="st">'first_name'</span>],</span>
<span id="cb6-6"><a aria-hidden="true" href="#cb6-6" tabindex="-1"></a> request.form[<span class="st">'last_name'</span>],</span>
<span id="cb6-7"><a aria-hidden="true" href="#cb6-7" tabindex="-1"></a> request.form[<span class="st">'phone'</span>],</span>
<span id="cb6-8"><a aria-hidden="true" href="#cb6-8" tabindex="-1"></a> request.form[<span class="st">'email'</span>]) <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span>
<span id="cb6-9"><a aria-hidden="true" href="#cb6-9" tabindex="-1"></a> <span class="cf">if</span> c.save(): <span class="op">&lt;</span><span class="dv">4</span><span class="op">&gt;</span></span>
<span id="cb6-10"><a aria-hidden="true" href="#cb6-10" tabindex="-1"></a> <span class="cf">return</span> c.__dict__</span>
<span id="cb6-11"><a aria-hidden="true" href="#cb6-11" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb6-12"><a aria-hidden="true" href="#cb6-12" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">"errors"</span>: c.errors}, <span class="dv">400</span></span></code></pre></div>
<figcaption><p>Updating a contact with our JSON API</p></figcaption>
</figure>
<ol>
<li><p>We handle <code>PUT</code> requests to the URL for a given
contact.</p></li>
<li><p>Look the contact up via the id passed in through the
path.</p></li>
<li><p>We update the contact’s data from the values included in the
request.</p></li>
<li><p>From here on the logic is identical to the
<code>json_contacts_create()</code> handler.</p></li>
</ol>
<p>Once again, thanks to the built-in functionality in Flask, simple to
implement.</p>
<p>Let’s look at deleting a contact now. This turns out to be even
simpler: as with the update handler we are going to look up the contact
by id, and then, well, delete it. At that point we can return a simple
JSON object indicating success.</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a><span class="at">@app.route</span>(<span class="st">"/api/v1/contacts/&lt;contact_id&gt;"</span>, methods<span class="op">=</span>[<span class="st">"DELETE"</span>]) <span class="op">&lt;</span><span class="dv">1</span><span class="op">&gt;</span></span>
<span id="cb7-2"><a aria-hidden="true" href="#cb7-2" tabindex="-1"></a><span class="kw">def</span> json_contacts_delete(contact_id<span class="op">=</span><span class="dv">0</span>):</span>
<span id="cb7-3"><a aria-hidden="true" href="#cb7-3" tabindex="-1"></a> contact <span class="op">=</span> Contact.find(contact_id)</span>
<span id="cb7-4"><a aria-hidden="true" href="#cb7-4" tabindex="-1"></a> contact.delete() <span class="op">&lt;</span><span class="dv">2</span><span class="op">&gt;</span></span>
<span id="cb7-5"><a aria-hidden="true" href="#cb7-5" tabindex="-1"></a> <span class="cf">return</span> jsonify({<span class="st">"success"</span>: <span class="va">True</span>}) <span class="op">&lt;</span><span class="dv">3</span><span class="op">&gt;</span></span></code></pre></div>
<figcaption><p>Deleting a contact with our JSON API</p></figcaption>
</figure>
<ol>
<li><p>We handle <code>DELETE</code> requests to the URL for a given
contact.</p></li>
<li><p>Look the contact up and invoke the <code>delete()</code> method
on it.</p></li>
<li><p>Return a simple JSON object indicating that the contact was
successfully deleted.</p></li>
</ol>
<p>And, with that, we have our simple little JSON Data API to live
alongside our regular web application, nicely separated out from the
main web application, so it can evolve separately as needed.</p>
<h3 id="_additional_data_api_considerations">Additional Data API
Considerations</h3>
<p>Now, we would have a lot more to do if we wanted to make this a
production ready JSON API. At minimum we would need to add:</p>
<ul>
<li><p>Rate limiting, important for any public-facing Data API to avoid
abusive clients.</p></li>
<li><p>An authentication mechanism. (We don’t have one for our web
application either!)</p></li>
<li><p>Support for pagination of our contact data.</p></li>
<li><p>Several small items, such as rendering a proper
<code>404 (Not Found)</code> response if someone makes a request with a
contact id that doesn’t exist.</p></li>
</ul>
<p>These topics are beyond the scope of this book, but we’d like to
focus on one in particular, authentication, in order to show the
difference between our hypermedia and JSON API. In order to secure our
application we need to add <em class="test">authentication</em>, some mechanism for
determining who a request is coming from, and also
<em class="test">authorization</em>, determining if they have the right to perform
the request.</p>
<p>We will set authorization aside for now and consider only
authentication.</p>
<h4 id="authentication-in-web-applications">Authentication in web applications</h4>
<p>In the HTML web application world, authentication has traditionally
been done via a login page that asks a user for their username (often
their email) and a password. This password is then checked against a
database of (hashed) passwords to establish that the user is who they
say they are. If the password is correct, then a <em class="test">session cookie</em>
is established, indicating who the user is. This cookie is then sent
with every request that the user makes to the web application, allowing
the application to know which user is making a given request.</p>
<div id="sidebar">
<div>
<div>
<p><strong>HTTP Cookies</strong></p>
</div>
<div>
<p>HTTP Cookies are kind of a strange feature of HTTP. In some ways they
violate the goal of remaining stateless, a major component of the
RESTful architecture: a server will often use a session cookie as an
index into state kept on the server “on the side”, such as a cache of
the last action performed by the user.</p>
</div>
</div>
</div>
<p>Nonetheless, cookies have proven extremely useful and so people tend
not to complain about this aspect of them too much (We are not sure what
our other options would be here!) An interesting example of pragmatism
gone (relatively) right in web development.</p>
<p>In comparison with the standard web application approach to
authentication, a JSON API will typically use some sort of <em class="test">token
based</em> authentication: an authentication token will be established
via a mechanism like OAuth, and that authentication token will then be
passed, often as an HTTP Header, with every request that a client
makes.</p>
<p>At a high level this is similar to what happens in normal web
application authentication: a token is established somehow and then that
token is part of every request. However, in practice, the mechanics tend
to be wildly different:</p>
<ul>
<li><p>Cookies are part of the HTTP specification and can be easily
<em class="test">set</em> by an HTTP Server.</p></li>
<li><p>JSON Authentication tokens, in contrast, often require elaborate
exchange mechanics like OAuth to be established.</p></li>
</ul>
<p>These differing mechanics for establishing authentication are yet
another good reason for splitting up our JSON and hypermedia APIs.</p>
<h3 id="_the_shape_of_our_two_apis">The “Shape” of Our Two APIs</h3>
<p>When we were building out our API, we noted that in many cases the
JSON API didn’t require as many end points as our hypermedia API did: we
didn’t need a <code>/contacts/new</code> handler, for example, to
provide a hypermedia representation for creating contacts.</p>
<p>Another aspect of our hypermedia API to consider was the performance
improvement we made: we pulled the total contact count out to a separate
endpoint and implemented the “Lazy Load” pattern, to improve the
perceived performance of our application.</p>
<p>Now, if we had both our hypermedia and JSON API sharing the same
paths, would we want to publish this API as a JSON endpoint as well?</p>
<p>Maybe, but maybe not. This was a pretty specific need for our web
application, and, absent a request from a user of our JSON API, it
doesn’t make sense to include it for JSON consumers.</p>
<p>And what if, by some miracle, the performance issues with
<code>Contact.count()</code> that we were addressing with the Lazy Load
pattern goes away? Well, in our Hypermedia-Driven Application we can
simply revert to the old code and include the count directly in the
request to <code>/contacts</code>. We can remove the
<code>contacts/count</code> endpoint and all the logic associated with
it. Because of the uniform interface of hypermedia, the system will
continue to work just fine.</p>
<p>But what if we had tied our JSON API and hypermedia API together, and
published <code>/contacts/count</code> as a supported end point for our
JSON API? In that case we couldn’t simply remove the endpoint: a
(non-hypermedia) client might be relying on it.</p>
<p>Once again you can see the flexibility of the hypermedia approach and
why separating your JSON API out from your hypermedia API lets you take
maximum advantage of that flexibility.</p>
<h3 id="_the_model_view_controller_mvc_paradigm">The Model View
Controller (MVC) Paradigm</h3>
<p>One thing you may have noticed about the handlers for our JSON API is
that they are relatively simple and regular. Most of the hard work of
updating data and so forth is done within the contact model itself: the
handlers act as simple connectors that provide a go-between the HTTP
requests and the model.</p>
<p>This is the ideal controller of the Model-View-Controller (MVC)
paradigm that was so popular in the early web: a controller should be
“thin”, with the model containing the majority of the logic in the
system.</p>
<div id="sidebar">
<div>
<div>
<p><strong>The Model View Controller pattern</strong></p>
</div>
<div>
<p>The Model View Controller design pattern is a classic architectural
pattern in software development, and was a major influence in early web
development. It is no longer emphasized as heavily, as web development
has split into frontend and backend camps, but most web developers are
still familiar with the idea.</p>
<p>Traditionally, the MVC pattern mapped into web development like
so:</p>
<ul>
<li><p>Model - A collection of “domain” classes that implement all the
logic and rules for the particular domain your application is designed
for. The model typically provides “resources” that are then presented to
clients as HTML “representations.”</p></li>
<li><p>View - Typically views would be some sort of client-side
templating system, and would render the aforementioned HTML
representation for a given Model instance.</p></li>
<li><p>Controller - The controller’s job is to take HTTP requests,
convert them into sensible requests to the Model and forward those
requests on to the appropriate Model objects. It then passes the HTML
representation back to the client as an HTTP response.</p></li>
</ul>
</div>
</div>
</div>
<p>Thin controllers make it easy to split your JSON and hypermedia APIs
out, because all the important logic lives in the domain model that is
shared by both. This allows you to evolve both separately, while still
keeping logic in sync with one another.</p>
<p>With properly built “thin” controllers and “fat” models, keeping two
separate APIs both in sync and yet still evolving separately is not as
difficult or as crazy as it might sound.</p>
<div id="html-note">
<div>
<h2 id="html-note-title">HTML Notes: Microformats</h2>
<p><a href="https://microformats.org/">Microformats</a> is a standard
for embedding machine-readable structured data in HTML. It uses classes
to mark certain elements as containing information to be extracted, with
conventions for extracting common properties like name, URL and photo
without classes. By adding these classes into the HTML representation of
an object, we allow the properties of the object to be recovered from
the HTML. For example, this simple HTML:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> class</span><span class="op">=</span><span class="st">"h-card"</span><span class="ot"> href</span><span class="op">=</span><span class="st">"https://john.example"</span><span class="dt">&gt;</span></span>
<span id="cb8-2"><a aria-hidden="true" href="#cb8-2" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">img</span><span class="ot"> src</span><span class="op">=</span><span class="st">"john.jpg"</span><span class="ot"> alt</span><span class="op">=</span><span class="st">""</span><span class="dt">&gt;</span> John Doe</span>
<span id="cb8-3"><a aria-hidden="true" href="#cb8-3" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span></code></pre></div>
</figure>
<p>can be parsed into this JSON-like structure by a microformats
parser:</p>
<figure>
<div class="sourceCode" id="cb9"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb9-1"><a aria-hidden="true" href="#cb9-1" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb9-2"><a aria-hidden="true" href="#cb9-2" tabindex="-1"></a> <span class="dt">"type"</span><span class="fu">:</span> <span class="ot">[</span><span class="st">"h-card"</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb9-3"><a aria-hidden="true" href="#cb9-3" tabindex="-1"></a> <span class="dt">"properties"</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb9-4"><a aria-hidden="true" href="#cb9-4" tabindex="-1"></a> <span class="dt">"name"</span><span class="fu">:</span> <span class="ot">[</span><span class="st">"John Doe"</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb9-5"><a aria-hidden="true" href="#cb9-5" tabindex="-1"></a> <span class="dt">"photo"</span><span class="fu">:</span> <span class="ot">[</span><span class="st">"john.jpg"</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb9-6"><a aria-hidden="true" href="#cb9-6" tabindex="-1"></a> <span class="dt">"url"</span><span class="fu">:</span> <span class="ot">[</span><span class="st">"https://john.example"</span><span class="ot">]</span></span>
<span id="cb9-7"><a aria-hidden="true" href="#cb9-7" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb9-8"><a aria-hidden="true" href="#cb9-8" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
</figure>
<p>Using a variety of properties and nested objects, we could mark up
every bit of information about a contact, for example, in a
machine-readable way.</p>
<p>As explained in the above chapter, trying to use the same mechanism
for human and machine interaction is not a good idea. Your human-facing
and machine-facing interfaces may end up being limited by each other. If
you want to expose domain-specific data and actions to users and
developers, a JSON API is a great option.</p>
<p>However, microformats are way easier to adopt. A protocol or standard
that requires websites to implement a JSON API has a high technical
barrier. In comparison, any website can be augmented with microformats
simply by adding a few classes. Other HTML-embedded data formats like
microdata, Open Graph are similarly easy to adopt. This makes
microformats useful for cross-website (dare we say <em class="test">web-scale</em>)
systems like the <a href="https://indieweb.org">IndieWeb</a>, which uses
it pervasively.</p>
</div>
</div>
</div>
</main>
</div>
<div class="chapter">
<h2 class="chapter-title">Hyperview: A Mobile Hypermedia</h2>
<main>
<details class="division-toc"><summary>Contents</summary>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_the_state_of_mobile_app_development">The State of Mobile App
Development</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#hypermedia-for-mobile-apps">Hypermedia for Mobile Apps</a>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_web_views">Web Views</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#hyperview">Hyperview</a>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#the-format">The format</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#the-client">The client</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_extensibility">Extensibility</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_which_hypermedia_architecture_should_you_use">Which Hypermedia
Architecture Should You Use?</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#introduction-to-hxml">Introduction to HXML</a>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#hello-world-">Hello World!</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#ui-elements">UI Elements</a>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#lists">Lists</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#images">Images</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#inputs">Inputs</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#styling">Styling</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#custom-elements">Custom elements</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_behaviors">Behaviors</a>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#actions">Actions</a>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#navigation-actions">Navigation actions</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#update-actions">Update actions</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#system-actions">System actions</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#custom-actions">Custom actions</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#triggers">Triggers</a>
<ul>
<li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_long_press">Long-press</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_load">Load</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_visible">Visible</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_refresh">Refresh</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_focus_blur_and_change">Focus, blur, and change</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#using-multiple-behaviors">Using multiple behaviors</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_summary">Summary</a>
</li></ul>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#_hypermedia_for_mobile">Hypermedia, for Mobile</a>
</li><li>
<a href="https://hypermedia.systems/hyperview-a-mobile-hypermedia/#html-note-title">Hypermedia Notes: Maximize Your Server-Side
Strengths</a>
</li></ul>
</details>
<div class="division-content">
<p>You may be forgiven for thinking the hypermedia architecture is
synonymous with the web, web browsers, and HTML. No doubt, the web is
the largest hypermedia system, and web browsers are the most popular
hypermedia client. The dominance of the web in discussions about
hypermedia make it easy to forget that hypermedia is a general concept,
and can be applied to all types of platforms and applications. In this
chapter, we will see the hypermedia architecture applied to a non-web
platform: native mobile applications.</p>
<p>Mobile as a platform has different constraints than the web. It
requires different trade-offs and design decisions. Nonetheless, the
concepts of hypermedia, HATEOAS, and REST can be directly applied to
build delightful mobile applications.</p>
<p>In this chapter we will cover shortcomings with the current state of
mobile app development, and how a hypermedia architecture can address
these problems. We will then look at a path toward hypermedia on mobile:
Hyperview, a mobile app framework that uses the hypermedia architecture.
We’ll conclude with an overview of HXML, the hypermedia format used by
Hyperview.</p>
<h2 id="_the_state_of_mobile_app_development">The State of Mobile App
Development</h2>
<p>Before we can discuss how to apply hypermedia to mobile platforms, we
need to understand how native mobile apps are commonly built. I’m using
the word “native” to refer to code written against an SDK provided by
the phone’s operating system (typically Android or iOS). This code is
packaged into an executable binary, and uploaded &amp; approved through
app stores controlled by Google and Apple. When users install or update
an app, they’re downloading this executable and running the code
directly on their device’s OS. In this way, mobile apps have a lot in
common with old-school desktop apps for Mac, Windows, or Linux. There is
one important difference between PC desktop apps of yesteryear and
today’s mobile apps. These days, almost all mobile apps are “networked”.
By networked, we mean the app needs to read and write data over the
Internet to deliver its core functionality. In other words, a networked
mobile app needs to implement the client-server architecture.</p>
<p>When implementing the client-server architecture, the developer needs
to make a decision: Should the app be designed as a thin client or thick
client? The current mobile ecosystems strongly push developers towards a
thick-client approach. Why? Remember, Android and iOS require that a
native mobile app be packaged and distributed as an executable binary.
There’s no way around it. Since the developer needs to write code to
package into an executable, it seems logical to implement some of the
app’s logic in that code. The code may as well initiate HTTP calls to
the server to retrieve data, and then render that data using the
platform’s UI libraries. Thus, developers are naturally led into a
thick-client pattern that looks something like this:</p>
<ul>
<li><p>The client contains code to make API requests to the server, and
code to translate those responses to UI updates</p></li>
<li><p>The server implements an HTTP API that speaks JSON, and knows
little about the state of the client</p></li>
</ul>
<p>Just like with SPAs on the web, this architecture has a big downside:
the app’s logic gets spread across the client and server. Sometimes,
this means that logic gets duplicated (like to validate form data).
Other times, the client and server each implement disjoint parts of the
app’s overall logic. To understand what the app does, a developer needs
to trace interactions between two very different codebases.</p>
<p>There’s another downside that affects mobile apps more than SPAs: API
churn. Remember, the app stores control how your app gets distributed
and updated. Users can even control if and when they get updated
versions of your app. As a mobile developer, you can’t assume that every
user will be on the latest version of your app. Your frontend code gets
fragmented across many versions, and now your backend needs to support
all of them.</p>
<h2 id="hypermedia-for-mobile-apps">Hypermedia for Mobile Apps</h2>
<p>We’ve seen that the hypermedia architecture can address the
shortcomings of SPAs on the web. But can hypermedia work for mobile apps
as well? The answer is yes!</p>
<p>Just like on the web, we can use hypermedia formats on mobile and let
it serve as the engine of application state. All of the logic is
controlled from the backend, rather than being spread between two
codebases. Hypermedia architecture also solves the annoying problem of
API churn on mobile apps. Since the backend serves a hypermedia response
containing both data and actions, there’s no way for the data and UI to
get out of sync. No more worries about backwards compatibility or
maintaining multiple API versions.</p>
<p>So how can you use hypermedia for your mobile app? There are two
approaches employing hypermedia to build &amp; ship native mobile apps
today:</p>
<ul>
<li><p>Web views, which wraps the trusty web platform in a mobile app
shell</p></li>
<li><p>Hyperview, a new hypermedia system we designed specifically for
mobile apps</p></li>
</ul>
<h3 id="_web_views">Web Views</h3>
<p>The simplest way to use hypermedia architecture on mobile is by
leveraging web technologies. Both Android and iOS SDKs provide “web
views”: chromeless web browsers that can be embedded in native apps.
Tools like Apache Cordova make it easy to take the URL of a website, and
spit out native iOS and Android apps based on web views. If you already
have a responsive web app, you can get a “native” mobile HDA for free.
Sounds too good to be true, right?</p>
<p>Of course, there is a fundamental limitation with this approach. The
web platform and mobile platforms have different capabilities and UX
conventions. HTML doesn’t natively support common UI patterns of mobile
apps. One of the biggest differences is around how each platform handles
navigation. On the web, navigation is page-based, with one page
replacing another and the browser providing back/forward buttons to
navigate the page history. On mobile, navigation is more complex, and
tuned for the physicality of gesture-based interactions.</p>
<ul>
<li><p>To drill down, screens slide on top of each other, forming stacks
of screens.</p></li>
<li><p>Tab bars at the top or bottom of the app allow switching between
various stacks of screens.</p></li>
<li><p>Modals slide up from the bottom of the app, covering the other
stacks and tab bar.</p></li>
<li><p>Unlike with web pages, all of these screens are still present in
memory, rendered and updating based on app state.</p></li>
</ul>
<p>The navigation architecture is a major difference between how mobile
and web apps function. But it’s not the only one. Many other UX patterns
are present in mobile apps, but are not natively supported on the
web:</p>
<ul>
<li><p>pull-to-refresh to refresh content in a screen</p></li>
<li><p>horizontal swipe on UI elements to reveal actions</p></li>
<li><p>sectioned lists with sticky headers</p></li>
</ul>
<p>While these interactions are not natively supported by web browsers,
they can be simulated with JS libraries. Of course, these libraries will
never have the same feel and performance as native gestures. And using
them usually requires embracing a JS-heavy SPA architecture like React.
This puts us back at square 1! To avoid using the typical thick-client
architecture of native mobile apps, we turned to a web view. The web
view allows us to use good-old hypermedia-based HTML. But to get the
desired look &amp; feel of a mobile app, we end up building a SPA in JS,
losing the benefits of Hypermedia in the process.</p>
<p>To build a mobile HDA that acts and feels like a native app, HTML
isn’t going to cut it. We need a format designed to represent the
interactions and patterns of native mobile apps. That’s exactly what
Hyperview does.</p>
<h3 id="hyperview">Hyperview</h3>
<p>Hyperview is an open-source hypermedia system that provides:</p>
<ul>
<li><p>A hypermedia format for defining mobile apps called HXML</p></li>
<li><p>A hypermedia client for HXML that works on iOS and
Android</p></li>
<li><p>Extension points in HXML and the client to customize the
framework for a given app</p></li>
</ul>
<h4 id="the-format">The format</h4>
<p>HXML was designed to feel familiar to web developers, used to working
with HTML. Thus the choice of XML for the base format. In addition to
familiar ergonomics, XML is compatible with server-side rendering
libraries. For example, Jinja2 is perfectly suited as a templating
library to render HXML. The familiarity of XML and the ease of
integration on the backend make it simple to adopt in both new and
existing codebases. Take a look at a “Hello World” app written in HXML.
The syntax should be familiar to anyone who’s worked with HTML:</p>
<figure>
<div class="sourceCode" id="cb1"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb1-1"><a aria-hidden="true" href="#cb1-1" tabindex="-1"></a>&lt;<span class="kw">doc</span><span class="ot"> xmlns=</span><span class="st">"https://hyperview.org/hyperview"</span>&gt;</span>
<span id="cb1-2"><a aria-hidden="true" href="#cb1-2" tabindex="-1"></a> &lt;<span class="kw">screen</span>&gt;</span>
<span id="cb1-3"><a aria-hidden="true" href="#cb1-3" tabindex="-1"></a> &lt;<span class="kw">styles</span> /&gt;</span>
<span id="cb1-4"><a aria-hidden="true" href="#cb1-4" tabindex="-1"></a> &lt;<span class="kw">body</span>&gt;</span>
<span id="cb1-5"><a aria-hidden="true" href="#cb1-5" tabindex="-1"></a> &lt;<span class="kw">header</span>&gt;</span>
<span id="cb1-6"><a aria-hidden="true" href="#cb1-6" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;My first app&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb1-7"><a aria-hidden="true" href="#cb1-7" tabindex="-1"></a> &lt;/<span class="kw">header</span>&gt;</span>
<span id="cb1-8"><a aria-hidden="true" href="#cb1-8" tabindex="-1"></a> &lt;<span class="kw">view</span>&gt;</span>
<span id="cb1-9"><a aria-hidden="true" href="#cb1-9" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;Hello World!&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb1-10"><a aria-hidden="true" href="#cb1-10" tabindex="-1"></a> &lt;/<span class="kw">view</span>&gt;</span>
<span id="cb1-11"><a aria-hidden="true" href="#cb1-11" tabindex="-1"></a> &lt;/<span class="kw">body</span>&gt;</span>
<span id="cb1-12"><a aria-hidden="true" href="#cb1-12" tabindex="-1"></a> &lt;/<span class="kw">screen</span>&gt;</span>
<span id="cb1-13"><a aria-hidden="true" href="#cb1-13" tabindex="-1"></a>&lt;/<span class="kw">doc</span>&gt;</span></code></pre></div>
<figcaption><p>Hello World</p></figcaption>
</figure>
<p>But HXML is not just a straight port of HTML with differently named
tags. In previous chapters, we’ve seen how htmx enhances HTML with a
handful of new attributes. These additions maintain the declarative
nature of HTML, while giving developers the power to create rich web
apps. In HXML, the concepts of htmx are built into the spec.
Specifically, HXML is not limited to “click a link” and “submit a form”
interactions like basic HTML. It supports a range of triggers and
actions for modifying the content on a screen. These interactions are
bundled together in a powerful concept of “behaviors.” Developers can
even define new behavior actions to add new capabilities to their app,
without the need for scripting. We will learn more about behaviors later
in this chapter.</p>
<h4 id="the-client">The client</h4>
<p>Hyperview provides an open-source HXML client library written in
React Native. With a little bit of configuration and a few steps on the
command line, this library compiles into native app binaries for iOS or
Android. Users install the app on their device via an app store. On
launch, the app makes an HTTP request to the configured URL, and renders
the HXML response as the first screen.</p>
<p>It may seem a little strange that developing a HDA using Hyperview
requires a single-purpose client binary. After all, we don’t ask users
to first download and install a binary to view a web app. No, users just
enter a URL in the address bar of a general-purpose web browser. A
single HTML client renders apps from any HTML server (<a class="ref" href="#fig-1clientmanyserver">[fig-1clientmanyserver]</a>).</p>
<figure id="fig-1clientmanyserver">
<div>
<div data-align="start">
<pre><code> ┌────────────┐
│ │
┌──────────┬─┐ │ SERVER │
├──────────┴─┤ │ │
│ │ └──▲─────────┘
│ │ │
│ │ │
│ ├───────┘ ┌────────────┐
│ │ │ │
│ CLIENT ├──────────▶ SERVER │
│ │ │ │
│ ├─────┐ └────────────┘
│ │ │
│ │ ┌────────────┐
│ │ │ │
└────────────┘ │ SERVER │
│ │
└────────────┘
</code></pre>
</div>
</div>
<figcaption><p>One HTML client, multiple HTML servers</p></figcaption>
</figure>
<p>It is theoretically possible to build an equivalent general-purpose
“Hyperview browser.” This HXML client would render apps from any HXML
server, and users would enter a URL to specify the app they want to use.
But iOS and Android are built around the concept of single-purpose apps.
Users expect to find and install apps from an app store, and launch them
from the home screen of their device. Hyperview embraces this
app-centric paradigm of today’s popular mobile platforms. That means
that the HXML client (app binary) renders its UI from a single
pre-configured HXML server (<a class="ref" href="#fig-1client1server">[fig-1client1server]</a>).</p>
<figure id="fig-1client1server">
<div>
<div data-align="start">
<pre><code>┌────────────┐
│ │ ┌────────────┐
│ │ │┌──────────┐│
│ SERVER │ ││ ││
│ │ ││┌───┐┌───┐││
│ ◀─────────┤ ││ │││
│ │ ││└───┘└───┘││
│ │ ││ App App ││
│ │ ││ ││
│ │ ││┌───┐┌───┐││
│ │ │└┴───┴┴───┴┘│
│ │ │ CLIENT │
│ │ └────────────┘
└────────────┘
</code></pre>
</div>
</div>
<figcaption><p>One HXML client, one HXML server</p></figcaption>
</figure>
<p>Luckily, developers do not need to write a HXML client from scratch;
the open-source client library does 99% of the work. And as we will see
in the next section, there are major benefits to controlling both the
client and server in a HDA.</p>
<h4 id="_extensibility">Extensibility</h4>
<p>To understand the benefits of Hyperview’s architecture, we need to
first discuss the drawbacks of the web architecture. On the web, any web
browser can render HTML from any web server. This level of compatibility
can only happen with well-defined standards such as HTML5. But defining
and evolving standards is a laborious process. For example, the W3C took
over 7 years to go from first draft to recommendation on the HTML5 spec.
It’s not surprising, given the level of thoughtfulness that needs to go
into a change that impacts so many people. But it means that progress
happens slowly. As a web developer, you may need to wait years for
browsers to gain widespread support for the feature you need.</p>
<p>So what are the benefits of Hyperview’s architecture? In a Hyperview
app, <em class="test">your</em> mobile app only renders HXML from <em class="test">your</em>
server. You don’t need to worry about compatibility between your server
and other mobile apps, or between your mobile app and other servers.
There is no standards body to consult. If you want to add a blink
feature to your mobile app, go ahead and implement a
<code>&lt;blink&gt;</code> element in the client, and start returning
<code>&lt;blink&gt;</code> elements in the HXML responses from your
server. In fact, the Hyperview client library was built with this type
of extensibility in mind. There are extension points for custom UI
elements and custom behavior actions. We expect and encourage developers
to use these extensions to make HXML more expressive and customized to
their app’s functionality.</p>
<p>And by extending the HXML format and client itself, there’s no need
for Hyperview to include a scripting layer in HXML. Features that
require client-side logic get “built-in” to the client binary. HXML
responses remain pure, with UI and interactions represented in
declarative XML.</p>
<h3 id="_which_hypermedia_architecture_should_you_use">Which Hypermedia
Architecture Should You Use?</h3>
<p>We’ve discussed two approaches for creating mobile apps using
hypermedia systems:</p>
<ul>
<li><p>create a backend that returns HTML, and serve it in a mobile app
through a web view</p></li>
<li><p>create a backend that returns HXML, and serve it in a mobile app
with the Hyperview client</p></li>
</ul>
<p>I purposefully described the two approaches in a way to highlight
their similarities. After all, they are both based on hypermedia
systems, just with different formats and clients. Both approaches solve
the fundamental issues with traditional, SPA-like mobile app
development:</p>
<ul>
<li><p>The backend controls the full state of the app.</p></li>
<li><p>Our app’s logic is all in one place.</p></li>
<li><p>The app always runs the latest version, there’s no API churn to
worry about.</p></li>
</ul>
<p>So which approach should you use for a mobile HDA? Based on our
experience building both types of apps, we believe the Hyperview
approach results in a better user experience. The web-view will always
feel out-of-place on iOS and Android; there’s just no good way to
replicate the patterns of navigation and interaction that mobile users
expect. Hyperview was created specifically to address the limitations of
thick-client and web view approaches. After the initial investment to
learn Hyperview, you’ll get all of the benefits of the Hypermedia
architecture, without the downsides of a degraded user experience.</p>
<p>Of course, if you already have a simple, mobile-friendly web app,
then using a web-view approach is sensible. You will certainly save time
from not having to serve your app as HXML in addition to HTML. But as we
will show at the end of this chapter, it doesn’t take a lot of work to
convert an existing Hypermedia-driven web app into a Hyperview mobile
app. But before we get there, we need to introduce the concepts of
elements and behaviors in Hyperview. Then, we’ll re-build our contacts
app in Hyperview.</p>
<div id="sidebar">
<div>
<div>
<p><strong>When Shouldn’t You Use Hypermedia to Build a Mobile
App?</strong></p>
</div>
<div>
<p>Hypermedia is not always the right choice to build a mobile app. Just
like on the web, apps that require highly dynamic UIs (such as a
spreadsheet application) are better implemented with client-side code.
Additionally, some apps need to function while fully offline. Since HDAs
require a server to render UI, offline-first mobile apps are not a good
fit for this architecture. However, just like on the web, developers can
use a hybrid approach to build their mobile app. The highly dynamic
screens can be built with complex client-side logic, while the less
dynamic screens can be built with web views or Hyperview. In this way,
developers can spend their <em class="test">complexity budget</em> on the core of the
application, and keep the simple screens simple.</p>
</div>
</div>
</div>
<h2 id="introduction-to-hxml">Introduction to HXML</h2>
<h3 id="hello-world-">Hello World!</h3>
<p>HXML was designed to feel natural to web developers coming from HTML.
Let’s take a closer look at the “Hello World” app defined in HXML:</p>
<figure>
<div class="sourceCode" id="cb4"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb4-1"><a aria-hidden="true" href="#cb4-1" tabindex="-1"></a>&lt;<span class="kw">doc</span><span class="ot"> xmlns=</span><span class="st">"https://hyperview.org/hyperview"</span>&gt; <span class="er">&lt;</span>1&gt;</span>
<span id="cb4-2"><a aria-hidden="true" href="#cb4-2" tabindex="-1"></a> &lt;<span class="kw">screen</span>&gt; <span class="er">&lt;</span>2&gt;</span>
<span id="cb4-3"><a aria-hidden="true" href="#cb4-3" tabindex="-1"></a> &lt;<span class="kw">styles</span> /&gt;</span>
<span id="cb4-4"><a aria-hidden="true" href="#cb4-4" tabindex="-1"></a> &lt;<span class="kw">body</span>&gt; <span class="er">&lt;</span>3&gt;</span>
<span id="cb4-5"><a aria-hidden="true" href="#cb4-5" tabindex="-1"></a> &lt;<span class="kw">header</span>&gt; <span class="er">&lt;</span>4&gt;</span>
<span id="cb4-6"><a aria-hidden="true" href="#cb4-6" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;My first app&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb4-7"><a aria-hidden="true" href="#cb4-7" tabindex="-1"></a> &lt;/<span class="kw">header</span>&gt;</span>
<span id="cb4-8"><a aria-hidden="true" href="#cb4-8" tabindex="-1"></a> &lt;<span class="kw">view</span>&gt; <span class="er">&lt;</span>5&gt;</span>
<span id="cb4-9"><a aria-hidden="true" href="#cb4-9" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;Hello World!&lt;/<span class="kw">text</span>&gt; <span class="er">&lt;</span>6&gt;</span>
<span id="cb4-10"><a aria-hidden="true" href="#cb4-10" tabindex="-1"></a> &lt;/<span class="kw">view</span>&gt;</span>
<span id="cb4-11"><a aria-hidden="true" href="#cb4-11" tabindex="-1"></a> &lt;/<span class="kw">body</span>&gt;</span>
<span id="cb4-12"><a aria-hidden="true" href="#cb4-12" tabindex="-1"></a> &lt;/<span class="kw">screen</span>&gt;</span>
<span id="cb4-13"><a aria-hidden="true" href="#cb4-13" tabindex="-1"></a>&lt;/<span class="kw">doc</span>&gt;</span></code></pre></div>
<figcaption><p>Hello World, revisited</p></figcaption>
</figure>
<ol>
<li><p>The root element of the HXML app</p></li>
<li><p>The element representing a screen of the app</p></li>
<li><p>The element representing the UI of the screen</p></li>
<li><p>The element representing the top header of the screen</p></li>
<li><p>A wrapper element around the content shown on the screen</p></li>
<li><p>The text content shown on the screen</p></li>
</ol>
<p>Nothing too strange here, right? Just like HTML, the syntax defines a
tree of elements using start tags (<code>&lt;screen&gt;</code>) and end
tags (<code>&lt;/screen&gt;</code>). Elements can contain other elements
(<code>&lt;view&gt;</code>) or text (<code>Hello World!</code>).
Elements can also be empty, represented with an empty tag
(<code>&lt;styles /&gt;</code>). However, you’ll notice that the names
of the HXML element are different from those in HTML. Let’s take a
closer look at each of those elements to understand what they do.</p>
<p><code>&lt;doc&gt;</code> is the root of the HXML app. Think of it as
equivalent to the <code>&lt;html&gt;</code> element in HTML. Note that
the <code>&lt;doc&gt;</code> element contains an attribute
<code>xmlns="https://hyperview.org/hyperview"</code>. This defines the
default namespace for the doc. Namespaces are a feature of XML that
allow one doc to contain elements defined by different developers. To
prevent conflicts when two developers use the same name for their
element, each developer defines a unique namespace. We will talk more
about namespaces when we discuss custom elements &amp; behaviors later
in this chapter. For now, it’s enough to know that elements in a HXML
doc without an explicit namespace are considered to be part of the
<code>https://hyperview.org/hyperview</code> namespace.</p>
<p><code>&lt;screen&gt;</code> represents the UI that gets rendered on a
single screen of a mobile app. It’s possible for one
<code>&lt;doc&gt;</code> to contain multiple <code>&lt;screen&gt;</code>
elements, but we won’t get into that now. Typically, a
<code>&lt;screen&gt;</code> element will contain elements that define
the content and styling of the screen.</p>
<p><code>&lt;styles&gt;</code> defines the styles of the UI on the
screen. We won’t get too much into styling in Hyperview in this chapter.
Suffice it to say, unlike HTML, Hyperview does not use a separate
language (CSS) to define styles. Instead, styling rules such as colors,
spacing, layout, and fonts are defined in HXML. These rules are then
explicitly referenced by UI elements, much like using classes in
CSS.</p>
<p><code>&lt;body&gt;</code> defines the actual UI of the screen. The
body includes all text, images, buttons, forms, etc that will be shown
to the user. This is equivalent to the <code>&lt;body&gt;</code> element
in HTML.</p>
<p><code>&lt;header&gt;</code> defines the header of the screen.
Typically in mobile apps, the header includes some navigation (like a
back button), and the title of the screen. It’s useful to define the
header separately from the rest of the body. Some mobile OSes will use a
different transition for the header than the rest of the screen
content.</p>
<p><code>&lt;view&gt;</code> is the basic building block for layouts and
structure within the screen’s body. Think of it like a
<code>&lt;div&gt;</code> in HTML. Note that unlike HTML
<code>&lt;div&gt;</code> elements, a <code>&lt;view&gt;</code> cannot
directly contain text.</p>
<p><code>&lt;text&gt;</code> elements are the only way to render text in
the UI. In this example, “Hello World” is contained within a
<code>&lt;text&gt;</code> element.</p>
<p>That’s all there is to define a basic “Hello World” app in HXML. Of
course, this isn’t very exciting. Let’s cover some other built-in
display elements.</p>
<h3 id="ui-elements">UI Elements</h3>
<h4 id="lists">Lists</h4>
<p>A very common pattern in mobile apps is to scroll through a list of
items. The physical properties of a phone screen (long &amp; vertical)
and the intuitive gesture of swiping a thumb up &amp; down makes this a
good choice for many screens.</p>
<p>HXML has dedicated elements for representing lists and items.</p>
<figure>
<div class="sourceCode" id="cb5"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb5-1"><a aria-hidden="true" href="#cb5-1" tabindex="-1"></a>&lt;<span class="kw">list</span>&gt; <span class="er">&lt;</span>1&gt;</span>
<span id="cb5-2"><a aria-hidden="true" href="#cb5-2" tabindex="-1"></a> &lt;<span class="kw">item</span><span class="ot"> key=</span><span class="st">"item1"</span>&gt; <span class="er">&lt;</span>2&gt;</span>
<span id="cb5-3"><a aria-hidden="true" href="#cb5-3" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;My first item&lt;/<span class="kw">text</span>&gt; <span class="er">&lt;</span>3&gt;</span>
<span id="cb5-4"><a aria-hidden="true" href="#cb5-4" tabindex="-1"></a> &lt;/<span class="kw">item</span>&gt;</span>
<span id="cb5-5"><a aria-hidden="true" href="#cb5-5" tabindex="-1"></a> &lt;<span class="kw">item</span><span class="ot"> key=</span><span class="st">"item2"</span>&gt;</span>
<span id="cb5-6"><a aria-hidden="true" href="#cb5-6" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;My second item&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb5-7"><a aria-hidden="true" href="#cb5-7" tabindex="-1"></a> &lt;/<span class="kw">item</span>&gt;</span>
<span id="cb5-8"><a aria-hidden="true" href="#cb5-8" tabindex="-1"></a>&lt;/<span class="kw">list</span>&gt;</span></code></pre></div>
<figcaption><p>List element</p></figcaption>
</figure>
<ol>
<li><p>Element representing a list</p></li>
<li><p>Element representing an item in the list, with a unique
key</p></li>
<li><p>The content of the item in the list.</p></li>
</ol>
<p>Lists are represented with two new elements. The
<code>&lt;list&gt;</code> wraps all of the items in the list. It can be
styled like a generic <code>&lt;view&gt;</code> (width, height, etc). A
<code>&lt;list&gt;</code> element only contains
<code>&lt;item&gt;</code> elements. Of course, these represent each
unique item in the list. Note that <code>&lt;item&gt;</code> is required
to have a <code>key</code> attribute, which is unique among all items in
the list.</p>
<p>You might be asking, “Why do we need a custom syntax for lists of
items? Can’t we just use a bunch of <code>&lt;view&gt;</code>
elements?”. Yes, for lists with a small number of items, using nested
<code>&lt;views&gt;</code> will work quite well. However, often the
number of items in a list can be long enough to require optimizations to
support smooth scrolling interactions. Consider browsing a feed of posts
in a social media app. As you keep scrolling through the feed, it’s not
unusual for the app to show hundreds if not thousands of posts. At any
time, you can flick your finger to scroll to almost any part of the
feed. Mobile devices tend to be memory-constrained. Keeping the
fully-rendered list of items in memory could consume more resources than
available. That’s why both iOS and Android provide APIs for optimized
list UIs. These APIs know which part of the list is currently on-screen.
To save memory, they clear out the non-visible list items, and recycle
the item UI objects to conserve memory. By using explicit
<code>&lt;list&gt;</code> and <code>&lt;item&gt;</code> elements in
HXML, the Hyperview client knows to use these optimized list APIs to
make your app more performant.</p>
<p>It’s also worth mentioning that HXML supports section lists. Section
lists are useful for building list-based UIs, where the items in the
list can be grouped for the user’s convenience. For example, a UI
showing a restaurant menu could group the offerings by dish type:</p>
<figure>
<div class="sourceCode" id="cb6"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb6-1"><a aria-hidden="true" href="#cb6-1" tabindex="-1"></a>&lt;<span class="kw">section-list</span>&gt; <span class="er">&lt;</span>1&gt;</span>
<span id="cb6-2"><a aria-hidden="true" href="#cb6-2" tabindex="-1"></a> &lt;<span class="kw">section</span>&gt; <span class="er">&lt;</span>2&gt;</span>
<span id="cb6-3"><a aria-hidden="true" href="#cb6-3" tabindex="-1"></a> &lt;<span class="kw">section-title</span>&gt; <span class="er">&lt;</span>3&gt;</span>
<span id="cb6-4"><a aria-hidden="true" href="#cb6-4" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;Appetizers&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb6-5"><a aria-hidden="true" href="#cb6-5" tabindex="-1"></a> &lt;/<span class="kw">section-title</span>&gt;</span>
<span id="cb6-6"><a aria-hidden="true" href="#cb6-6" tabindex="-1"></a> &lt;<span class="kw">item</span><span class="ot"> key=</span><span class="st">"1"</span>&gt; <span class="er">&lt;</span>4&gt;</span>
<span id="cb6-7"><a aria-hidden="true" href="#cb6-7" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;French Fries&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb6-8"><a aria-hidden="true" href="#cb6-8" tabindex="-1"></a> &lt;/<span class="kw">item</span>&gt;</span>
<span id="cb6-9"><a aria-hidden="true" href="#cb6-9" tabindex="-1"></a> &lt;<span class="kw">item</span><span class="ot"> key=</span><span class="st">"2"</span>&gt;</span>
<span id="cb6-10"><a aria-hidden="true" href="#cb6-10" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;Onion Rings&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb6-11"><a aria-hidden="true" href="#cb6-11" tabindex="-1"></a> &lt;/<span class="kw">item</span>&gt;</span>
<span id="cb6-12"><a aria-hidden="true" href="#cb6-12" tabindex="-1"></a> &lt;/<span class="kw">section</span>&gt;</span>
<span id="cb6-13"><a aria-hidden="true" href="#cb6-13" tabindex="-1"></a></span>
<span id="cb6-14"><a aria-hidden="true" href="#cb6-14" tabindex="-1"></a> &lt;<span class="kw">section</span>&gt; <span class="er">&lt;</span>5&gt;</span>
<span id="cb6-15"><a aria-hidden="true" href="#cb6-15" tabindex="-1"></a> &lt;<span class="kw">section-title</span>&gt;</span>
<span id="cb6-16"><a aria-hidden="true" href="#cb6-16" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;Entrees&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb6-17"><a aria-hidden="true" href="#cb6-17" tabindex="-1"></a> &lt;/<span class="kw">section-title</span>&gt;</span>
<span id="cb6-18"><a aria-hidden="true" href="#cb6-18" tabindex="-1"></a> &lt;<span class="kw">item</span><span class="ot"> key=</span><span class="st">"3"</span>&gt;</span>
<span id="cb6-19"><a aria-hidden="true" href="#cb6-19" tabindex="-1"></a> &lt;<span class="kw">text</span>&gt;Burger&lt;/<span class="kw">text</span>&gt;</span>
<span id="cb6-20"><a aria-hidden="true" href="#cb6-20" tabindex="-1"></a> &lt;/<span class="kw">item</span>&gt;</span>
<span id="cb6-21"><a aria-hidden="true" href="#cb6-21" tabindex="-1"></a> &lt;/<span class="kw">section</span>&gt;</span>
<span id="cb6-22"><a aria-hidden="true" href="#cb6-22" tabindex="-1"></a>&lt;/<span class="kw">section-list</span>&gt;</span></code></pre></div>
<figcaption><p>Section list element</p></figcaption>
</figure>
<ol>
<li><p>Element representing a list with sections</p></li>
<li><p>The first section of appetizer offerings</p></li>
<li><p>Element for the title of the section, rendering the text
“Appetizers”</p></li>
<li><p>An item representing an appetizer</p></li>
<li><p>A section for entree offerings</p></li>
</ol>
<p>You’ll notice a couple of differences between
<code>&lt;list&gt;</code> and <code>&lt;section-list&gt;</code>. The
section list element only contains <code>&lt;section&gt;</code>
elements, representing a group of items. A section can contain a
<code>&lt;section-title&gt;</code> element. This is used to render some
UI that acts as the header of the section. This header is “sticky”,
meaning it stays on screen while scrolling through items that belong to
the corresponding section. Finally, <code>&lt;item&gt;</code> elements
act the same as in the regular list, but can only appear within a
<code>&lt;section&gt;</code>.</p>
<h4 id="images">Images</h4>
<p>Showing images in Hyperview is pretty similar to HTML, but there are
a few differences.</p>
<figure>
<div class="sourceCode" id="cb7"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb7-1"><a aria-hidden="true" href="#cb7-1" tabindex="-1"></a>&lt;<span class="kw">image</span><span class="ot"> source=</span><span class="st">"/profiles/1.jpg"</span><span class="ot"> style=</span><span class="st">"avatar"</span> /&gt;</span></code></pre></div>
<figcaption><p>Image element</p></figcaption>
</figure>
<p>The <code>source</code> attribute specifies how to load the image.
Like in HTML, the source can be an absolute or relative URL.
Additionally, the source can be an encoded data URI, for example
<code>data:image/png;base64,iVBORw</code>. However, the source can also
be a “local” URL, referring to an image that is bundled as an asset in
the mobile app. The local URL is prefixed with <code>./</code>:</p>
<figure>
<div class="sourceCode" id="cb8"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb8-1"><a aria-hidden="true" href="#cb8-1" tabindex="-1"></a>&lt;<span class="kw">image</span><span class="ot"> source=</span><span class="st">"./logo.png"</span><span class="ot"> style=</span><span class="st">"logo"</span> /&gt;</span></code></pre></div>
<figcaption><p>Image element, pointing to local source</p></figcaption>
</figure>
<p>Using Local URLs is an optimization. Since the images are on the
mobile device, they don’t require a network request and will appear
quickly. However, bundling the image with the mobile app binary
increases the binary size. Using local images is a good trade-off for
images that are frequently accessed but rarely change. Good examples
include the app logo, or common button icons.</p>
<p>The other thing to note is the presence of the <code>style</code>
attribute on the <code>&lt;image&gt;</code> element. In HXML, images are
required to have a style that has rules for the i
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment