Contact Form
Kontaktformular mit Validierung und Formular-Submission.
Component HTML
html
<div class="max-w-2xl mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-8 text-center">{{title}}</h2>
<div
data-island="contact-form"
data-island-props='{
"submitUrl": "{{submitUrl}}",
"successMessage": "{{successMessage}}"
}'
>
<!-- Form Island rendert hier -->
</div>
</div>Island Code
javascript
// islands/contact-form.js
export default class ContactFormIsland {
constructor(element, props = {}) {
this.element = element;
this.props = props;
this.errors = {};
this.isSubmitting = false;
this.init();
}
init() {
this.render();
this.attachEvents();
}
render() {
const { successMessage } = this.props;
this.element.innerHTML = `
<form class="bg-white p-8 rounded-lg shadow-lg border border-gray-200">
<div class="success-message hidden mb-6 p-4 bg-green-100 border border-green-400 text-green-700 rounded">
${successMessage}
</div>
<div class="mb-6">
<label for="name" class="block text-sm font-semibold text-gray-700 mb-2">
Name *
</label>
<input
type="text"
id="name"
name="name"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
required
/>
<span class="error-name text-red-600 text-sm hidden"></span>
</div>
<div class="mb-6">
<label for="email" class="block text-sm font-semibold text-gray-700 mb-2">
E-Mail *
</label>
<input
type="email"
id="email"
name="email"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
required
/>
<span class="error-email text-red-600 text-sm hidden"></span>
</div>
<div class="mb-6">
<label for="message" class="block text-sm font-semibold text-gray-700 mb-2">
Nachricht *
</label>
<textarea
id="message"
name="message"
rows="5"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
required
></textarea>
<span class="error-message text-red-600 text-sm hidden"></span>
</div>
<button
type="submit"
class="w-full px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<span class="submit-text">Absenden</span>
<span class="loading-text hidden">Wird gesendet...</span>
</button>
</form>
`;
}
attachEvents() {
const form = this.element.querySelector('form');
form.addEventListener('submit', (e) => this.handleSubmit(e));
// Real-time validation
['name', 'email', 'message'].forEach(field => {
const input = this.element.querySelector(`#${field}`);
input.addEventListener('blur', () => this.validateField(field));
});
}
validateField(field) {
const input = this.element.querySelector(`#${field}`);
const errorSpan = this.element.querySelector(`.error-${field}`);
const value = input.value.trim();
let error = '';
if (!value) {
error = 'Dieses Feld ist erforderlich';
} else if (field === 'email' && !this.isValidEmail(value)) {
error = 'Bitte geben Sie eine gültige E-Mail-Adresse ein';
} else if (field === 'message' && value.length < 10) {
error = 'Nachricht muss mindestens 10 Zeichen lang sein';
}
if (error) {
this.errors[field] = error;
errorSpan.textContent = error;
errorSpan.classList.remove('hidden');
input.classList.add('border-red-500');
} else {
delete this.errors[field];
errorSpan.classList.add('hidden');
input.classList.remove('border-red-500');
}
return !error;
}
isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
async handleSubmit(e) {
e.preventDefault();
// Validate all fields
const isValid = ['name', 'email', 'message'].every(field =>
this.validateField(field)
);
if (!isValid || this.isSubmitting) return;
this.isSubmitting = true;
this.updateSubmitButton(true);
try {
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
const response = await fetch(this.props.submitUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (response.ok) {
this.showSuccess();
e.target.reset();
} else {
throw new Error('Submission failed');
}
} catch (error) {
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.');
} finally {
this.isSubmitting = false;
this.updateSubmitButton(false);
}
}
updateSubmitButton(loading) {
const submitBtn = this.element.querySelector('button[type="submit"]');
const submitText = this.element.querySelector('.submit-text');
const loadingText = this.element.querySelector('.loading-text');
submitBtn.disabled = loading;
submitText.classList.toggle('hidden', loading);
loadingText.classList.toggle('hidden', !loading);
}
showSuccess() {
const successMsg = this.element.querySelector('.success-message');
successMsg.classList.remove('hidden');
setTimeout(() => {
successMsg.classList.add('hidden');
}, 5000);
}
destroy() {
// Cleanup
}
}Was du lernst
- Form Handling: Formulardaten verwalten und absenden
- Validation: Real-time Validierung mit Error Messages
- Async Operations: API-Calls mit fetch
- Loading States: UI-Feedback während async Operations
- Success Handling: Success Messages anzeigen