Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-06-02 - Form Submitter Loading States in Vanilla JS
**Learning:** When dealing with asynchronous form submissions and applying loading states (disabling buttons, changing text) in vanilla JavaScript, it's crucial to utilize `e.submitter` rather than relying on `document.getElementById` or guessing the submit button. This guarantees we update the specific button that triggered the action, especially if forms have multiple submission points. Additionally, utilizing `finally` blocks guarantees UI recovery even if errors occur, and safely null-checking the submitter prevents unexpected TypeErrors.
**Action:** Always capture the submit button via `const submitBtn = e.submitter;` and implement state modifications within a try/finally structure.
32 changes: 32 additions & 0 deletions web-demo/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,19 @@ class ClimaAI {
e.preventDefault();
const email = document.getElementById('loginEmail').value;
const password = document.getElementById('loginPassword').value;
const submitBtn = e.submitter;
let originalText = '';

if (submitBtn) {
originalText = submitBtn.innerHTML;
submitBtn.innerHTML = 'Signing In...';
submitBtn.disabled = true;
submitBtn.setAttribute('aria-busy', 'true');
}

try {
this.showToast('Logging in...', 'info');
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate network delay
const response = await api.login(email, password);
this.user = response.user;
this.showToast('Welcome back! 🌀️', 'success');
Expand All @@ -155,6 +165,12 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Login failed', 'error');
} finally {
if (submitBtn) {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
submitBtn.removeAttribute('aria-busy');
}
}
}

Expand All @@ -163,9 +179,19 @@ class ClimaAI {
const name = document.getElementById('registerName').value;
const email = document.getElementById('registerEmail').value;
const password = document.getElementById('registerPassword').value;
const submitBtn = e.submitter;
let originalText = '';

if (submitBtn) {
originalText = submitBtn.innerHTML;
submitBtn.innerHTML = 'Signing Up...';
submitBtn.disabled = true;
submitBtn.setAttribute('aria-busy', 'true');
}

try {
this.showToast('Creating account...', 'info');
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate network delay
const response = await api.register(email, password, name);
this.user = response.user;
this.showToast('Account created! Welcome! πŸŽ‰', 'success');
Expand All @@ -174,6 +200,12 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Registration failed', 'error');
} finally {
if (submitBtn) {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
submitBtn.removeAttribute('aria-busy');
}
}
}

Expand Down