ok
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
padding: 0.85rem 1rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-md);
|
||||
|
||||
|
||||
/* Aussehen & Typografie */
|
||||
background-color: var(--color-surface);
|
||||
color: var(--color-text);
|
||||
@@ -46,14 +46,18 @@
|
||||
position: absolute;
|
||||
top: 50%; /* Vertikal zentrieren (Schritt 1) */
|
||||
left: 1rem; /* Linken Abstand wie beim Input-Padding halten */
|
||||
transform: translateY(-50%); /* Vertikal zentrieren (Schritt 2 - Feinjustierung) */
|
||||
transform: translateY(
|
||||
-50%
|
||||
); /* Vertikal zentrieren (Schritt 2 - Feinjustierung) */
|
||||
|
||||
border-radius: 4px;
|
||||
/* Aussehen & Typografie */
|
||||
color: var(--color-text-light);
|
||||
background-color: var(--color-surface); /* WICHTIG: Überdeckt die Input-Linie beim Schweben */
|
||||
background-color: var(
|
||||
--color-surface
|
||||
); /* WICHTIG: Überdeckt die Input-Linie beim Schweben */
|
||||
padding: 0 0.25rem;
|
||||
|
||||
|
||||
/* Verhalten */
|
||||
transition: all 0.2s ease-out; /* Animiert alle Änderungen (top, font-size, color) */
|
||||
pointer-events: none; /* Erlaubt Klicks "durch" das Label auf das Input-Feld darunter */
|
||||
@@ -75,7 +79,9 @@
|
||||
.form-input:focus ~ .form-label,
|
||||
.form-input:not(:placeholder-shown) ~ .form-label {
|
||||
top: 0;
|
||||
transform: translateY(-50%); /* Bleibt für die Zentrierung auf der Border-Linie wichtig */
|
||||
transform: translateY(
|
||||
-50%
|
||||
); /* Bleibt für die Zentrierung auf der Border-Linie wichtig */
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
@@ -87,12 +93,12 @@
|
||||
* und die richtige Farbe bekommt.
|
||||
*/
|
||||
.form-input:-webkit-autofill,
|
||||
.form-input:-webkit-autofill:hover,
|
||||
.form-input:-webkit-autofill:focus,
|
||||
.form-input:-webkit-autofill:hover,
|
||||
.form-input:-webkit-autofill:focus,
|
||||
.form-input:-webkit-autofill:active {
|
||||
/* OPTIONAL: Überschreibt den unschönen gelben/blauen Autofill-Hintergrund */
|
||||
-webkit-box-shadow: 0 0 0 30px var(--color-surface) inset !important;
|
||||
-webkit-text-fill-color: var(--color-text) !important;
|
||||
/* OPTIONAL: Überschreibt den unschönen gelben/blauen Autofill-Hintergrund */
|
||||
-webkit-box-shadow: 0 0 0 30px var(--color-surface) inset !important;
|
||||
-webkit-text-fill-color: var(--color-text) !important;
|
||||
}
|
||||
|
||||
.form-input:-webkit-autofill ~ .form-label {
|
||||
@@ -100,4 +106,18 @@
|
||||
transform: translateY(-50%);
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.required-indicator {
|
||||
color: var(--color-danger);
|
||||
font-weight: bold;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* Styling für die Fehlermeldung */
|
||||
.error-message {
|
||||
color: var(--color-danger);
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.25rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
<div class="form-field">
|
||||
<input
|
||||
[type]="type"
|
||||
class="form-input"
|
||||
[id]="controlId"
|
||||
placeholder=" "
|
||||
[disabled]="disabled"
|
||||
[(ngModel)]="value"
|
||||
(ngModelChange)="onChange($event)"
|
||||
(blur)="onTouched()">
|
||||
|
||||
<label [for]="controlId" class="form-label">{{ label }}</label>
|
||||
<!-- /src/app/shared/components/form/form-field/form-field.component.html -->
|
||||
|
||||
<div class="form-field-wrapper">
|
||||
<div class="form-field">
|
||||
<input
|
||||
[type]="type"
|
||||
class="form-input"
|
||||
[id]="controlId"
|
||||
placeholder=" "
|
||||
[disabled]="disabled"
|
||||
[(ngModel)]="value"
|
||||
(ngModelChange)="onChange($event)"
|
||||
(blur)="onTouched()">
|
||||
|
||||
<label [for]="controlId" class="form-label">
|
||||
{{ label }}
|
||||
<!-- Der Indikator wird jetzt nur bei Bedarf angezeigt -->
|
||||
<span *ngIf="isRequired" class="required-indicator">*</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Anzeige für Validierungsfehler -->
|
||||
<div *ngIf="showErrors && errorMessage" class="error-message">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Component, Input, forwardRef } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AbstractControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
|
||||
import { AbstractControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, Validators } from '@angular/forms'; // Validators importieren
|
||||
|
||||
@Component({
|
||||
selector: 'app-form-field',
|
||||
@@ -10,10 +10,10 @@ import { AbstractControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule }
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule // <-- WICHTIG: Hinzufügen, um mit AbstractControl zu arbeiten
|
||||
ReactiveFormsModule
|
||||
],
|
||||
templateUrl: './form-field.component.html',
|
||||
styleUrl: './form-field.component.css',
|
||||
styleUrls: ['./form-field.component.css'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
@@ -23,44 +23,42 @@ import { AbstractControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule }
|
||||
],
|
||||
})
|
||||
export class FormFieldComponent {
|
||||
// --- KORREKTUR: Erweitere die erlaubten Typen ---
|
||||
@Input() type: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' = 'text';
|
||||
@Input() label: string = '';
|
||||
|
||||
// Neuer Input, um das FormControl für die Fehleranzeige zu erhalten
|
||||
@Input() control?: AbstractControl;
|
||||
@Input() showErrors = true; // Standardmäßig Fehler anzeigen
|
||||
@Input() control?: AbstractControl | null;
|
||||
@Input() showErrors = true;
|
||||
|
||||
controlId = `form-field-${Math.random().toString(36).substring(2, 9)}`;
|
||||
|
||||
// --- Eigenschaften & Methoden für ControlValueAccessor ---
|
||||
value: string | number = '';
|
||||
onChange: (value: any) => void = () => {};
|
||||
onTouched: () => void = () => {};
|
||||
disabled = false;
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
// NEU: Getter, der automatisch prüft, ob das Feld ein Pflichtfeld ist.
|
||||
get isRequired(): boolean {
|
||||
if (!this.control) {
|
||||
return false;
|
||||
}
|
||||
// hasValidator prüft, ob ein bestimmter Validator auf dem Control gesetzt ist.
|
||||
return this.control.hasValidator(Validators.required);
|
||||
}
|
||||
|
||||
// Hilfsfunktion für das Template, um Fehler zu finden
|
||||
get errorMessage(): string | null {
|
||||
if (!this.control || !this.control.errors || (!this.control.touched && !this.control.dirty)) {
|
||||
return null;
|
||||
}
|
||||
if (this.control.hasError('required')) return 'Dieses Feld ist erforderlich.';
|
||||
if (this.control.hasError('email')) return 'Bitte geben Sie eine gültige E-Mail-Adresse ein.';
|
||||
if (this.control.hasError('min')) return `Der Wert muss mindestens ${this.control.errors['min'].min} sein.`;
|
||||
// ... weitere Fehlermeldungen hier
|
||||
const errors = this.control.errors;
|
||||
if (errors['required']) return 'Dieses Feld ist erforderlich.';
|
||||
if (errors['email']) return 'Bitte geben Sie eine gültige E-Mail-Adresse ein.';
|
||||
if (errors['min']) return `Der Wert muss mindestens ${errors['min'].min} sein.`;
|
||||
if (errors['max']) return `Der Wert darf maximal ${errors['max'].max} sein.`;
|
||||
if (errors['minlength']) return `Mindestens ${errors['minlength'].requiredLength} Zeichen erforderlich.`;
|
||||
|
||||
return 'Ungültige Eingabe.';
|
||||
}
|
||||
|
||||
writeValue(value: any): void { this.value = value; }
|
||||
registerOnChange(fn: any): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: any): void { this.onTouched = fn; }
|
||||
setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled; }
|
||||
}
|
||||
@@ -76,4 +76,18 @@ border-radius: 4px;
|
||||
top: 0;
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.required-indicator {
|
||||
color: var(--color-danger);
|
||||
font-weight: bold;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
/* Styling für die Fehlermeldung */
|
||||
.error-message {
|
||||
color: var(--color-danger);
|
||||
font-size: 0.875rem;
|
||||
margin-top: 0.25rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
<div class="form-field">
|
||||
<textarea
|
||||
class="form-input"
|
||||
[id]="controlId"
|
||||
placeholder=" "
|
||||
[rows]="rows"
|
||||
[(ngModel)]="value"
|
||||
(ngModelChange)="onChange($event)"
|
||||
(blur)="onTouched()"
|
||||
[disabled]="disabled"></textarea>
|
||||
<!-- /src/app/shared/components/form/form-textarea/form-textarea.component.html -->
|
||||
|
||||
<div class="form-field-wrapper">
|
||||
<div class="form-field">
|
||||
<textarea
|
||||
class="form-input"
|
||||
[id]="controlId"
|
||||
[rows]="rows"
|
||||
placeholder=" "
|
||||
[disabled]="disabled"
|
||||
[(ngModel)]="value"
|
||||
(ngModelChange)="onChange($event)"
|
||||
(blur)="onTouched()"></textarea>
|
||||
|
||||
<label [for]="controlId" class="form-label">
|
||||
{{ label }}
|
||||
<span *ngIf="isRequired" class="required-indicator">*</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label [for]="controlId" class="form-label">{{ label }}</label>
|
||||
<div *ngIf="showErrors && errorMessage" class="error-message">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,38 +1,54 @@
|
||||
// /src/app/shared/components/form/form-textarea/form-textarea.component.ts
|
||||
|
||||
import { Component, Input, forwardRef } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AbstractControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-form-textarea',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
FormsModule // Wichtig für [(ngModel)]
|
||||
],
|
||||
imports: [ CommonModule, FormsModule, ReactiveFormsModule ],
|
||||
templateUrl: './form-textarea.component.html',
|
||||
styleUrl: './form-textarea.component.css',
|
||||
styleUrls: ['./form-textarea.component.css'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => FormTextareaComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class FormTextareaComponent implements ControlValueAccessor {
|
||||
export class FormTextareaComponent {
|
||||
@Input() label: string = '';
|
||||
@Input() rows = 3; // Standardanzahl der Zeilen
|
||||
@Input() rows: number = 4;
|
||||
|
||||
// NEU: Hinzufügen des 'control' Inputs, genau wie in form-field
|
||||
@Input() control?: AbstractControl | null;
|
||||
|
||||
@Input() showErrors = true;
|
||||
|
||||
// Eindeutige ID für die Verknüpfung
|
||||
controlId = `form-textarea-${Math.random().toString(36).substring(2)}`;
|
||||
|
||||
// --- Logik für ControlValueAccessor ---
|
||||
controlId = `form-textarea-${Math.random().toString(36).substring(2, 9)}`;
|
||||
value: string = '';
|
||||
onChange: (value: any) => void = () => {};
|
||||
onTouched: () => void = () => {};
|
||||
disabled = false;
|
||||
|
||||
get isRequired(): boolean {
|
||||
if (!this.control) return false;
|
||||
return this.control.hasValidator(Validators.required);
|
||||
}
|
||||
|
||||
get errorMessage(): string | null {
|
||||
if (!this.control || !this.control.errors || (!this.control.touched && !this.control.dirty)) {
|
||||
return null;
|
||||
}
|
||||
const errors = this.control.errors;
|
||||
if (errors['required']) return 'Dieses Feld ist erforderlich.';
|
||||
if (errors['minlength']) return `Mindestens ${errors['minlength'].requiredLength} Zeichen erforderlich.`;
|
||||
|
||||
return 'Ungültige Eingabe.';
|
||||
}
|
||||
|
||||
writeValue(value: any): void { this.value = value; }
|
||||
registerOnChange(fn: any): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: any): void { this.onTouched = fn; }
|
||||
|
||||
Reference in New Issue
Block a user