connect formgroup with step 1 from the event wizzard
This commit is contained in:
parent
95aa8d6b11
commit
4a69d916c6
|
@ -21,7 +21,7 @@
|
||||||
<div class="flex-grow overflow-y-auto px-4 sm:px-8 py-8">
|
<div class="flex-grow overflow-y-auto px-4 sm:px-8 py-8">
|
||||||
<div class="w-full max-w-4xl mx-auto">
|
<div class="w-full max-w-4xl mx-auto">
|
||||||
@if (currentStep() === 0) {
|
@if (currentStep() === 0) {
|
||||||
<app-basic-step></app-basic-step>
|
<app-basic-step [form]="form"></app-basic-step>
|
||||||
}
|
}
|
||||||
@if (currentStep() === 1) {
|
@if (currentStep() === 1) {
|
||||||
<app-tickets-step></app-tickets-step>
|
<app-tickets-step></app-tickets-step>
|
||||||
|
|
|
@ -4,8 +4,8 @@ import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
signal,
|
signal,
|
||||||
WritableSignal,
|
WritableSignal,
|
||||||
OnInit,
|
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
|
||||||
import { BasicStepComponent } from './steps/basic-step.component';
|
import { BasicStepComponent } from './steps/basic-step.component';
|
||||||
import { TicketsStepComponent } from './steps/tickets-step.component';
|
import { TicketsStepComponent } from './steps/tickets-step.component';
|
||||||
|
@ -18,13 +18,19 @@ import { TicketsStepComponent } from './steps/tickets-step.component';
|
||||||
templateUrl: './create-event.component.html',
|
templateUrl: './create-event.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class CreateEventComponent implements OnInit {
|
export class CreateEventComponent {
|
||||||
public currentStep: WritableSignal<number> = signal(0);
|
public currentStep: WritableSignal<number> = signal(0);
|
||||||
public readonly steps: string[] = ['Basic', 'Tickets', 'Review'];
|
public readonly steps: string[] = ['Basic', 'Tickets', 'Review'];
|
||||||
|
public form!: FormGroup;
|
||||||
|
|
||||||
public constructor() {}
|
public constructor(private readonly formBuilder: FormBuilder) {
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
public ngOnInit(): void {}
|
eventTitle: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
|
eventLocation: ['', Validators.required],
|
||||||
|
eventDate: ['', Validators.required],
|
||||||
|
eventTime: ['', Validators.required],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public getStepContent(index: number): string {
|
public getStepContent(index: number): string {
|
||||||
if (index < this.currentStep()) {
|
if (index < this.currentStep()) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<form class="flex flex-col">
|
<form [formGroup]="form()" class="flex flex-col">
|
||||||
<h1 class="text-2xl font-bold mb-6">Event Basic Information</h1>
|
<h1 class="text-2xl font-bold mb-6">Event Basic Information</h1>
|
||||||
|
|
||||||
<label class="form-control w-full mb-4">
|
<label class="form-control w-full mb-4">
|
||||||
|
@ -7,6 +7,8 @@
|
||||||
<span class="label-text-alt">Make it catchy and descriptive</span>
|
<span class="label-text-alt">Make it catchy and descriptive</span>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
formControlName="eventTitle"
|
||||||
|
[ngClass]="getInputClass('eventTitle')"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter your event title"
|
placeholder="Enter your event title"
|
||||||
class="input input-bordered w-full" />
|
class="input input-bordered w-full" />
|
||||||
|
@ -18,6 +20,7 @@
|
||||||
|
|
||||||
<div class="form-control w-full">
|
<div class="form-control w-full">
|
||||||
<app-dropdown
|
<app-dropdown
|
||||||
|
formControlName="eventLocation"
|
||||||
label="Event Location"
|
label="Event Location"
|
||||||
placeholder="Search for a Location"
|
placeholder="Search for a Location"
|
||||||
hintTopRight="Select or create a new Location by using the dropdown"
|
hintTopRight="Select or create a new Location by using the dropdown"
|
||||||
|
@ -34,7 +37,12 @@
|
||||||
<span class="label-text">Date</span>
|
<span class="label-text">Date</span>
|
||||||
<span class="label-text-alt">Select the event date</span>
|
<span class="label-text-alt">Select the event date</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="date" id="event-date" class="input input-bordered w-full" />
|
<input
|
||||||
|
formControlName="eventDate"
|
||||||
|
[ngClass]="getInputClass('eventDate')"
|
||||||
|
type="date"
|
||||||
|
id="event-date"
|
||||||
|
class="input input-bordered w-full" />
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text-alt"></span>
|
<span class="label-text-alt"></span>
|
||||||
<span class="label-text-alt"></span>
|
<span class="label-text-alt"></span>
|
||||||
|
@ -46,12 +54,21 @@
|
||||||
<span class="label-text">Time</span>
|
<span class="label-text">Time</span>
|
||||||
<span class="label-text-alt">Choose the event time</span>
|
<span class="label-text-alt">Choose the event time</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="time" id="event-time" class="input input-bordered w-full" />
|
<input
|
||||||
|
formControlName="eventTime"
|
||||||
|
[ngClass]="getInputClass('eventTime')"
|
||||||
|
type="time"
|
||||||
|
id="event-time"
|
||||||
|
class="input input-bordered w-full" />
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text-alt"></span>
|
<span class="label-text-alt"></span>
|
||||||
<span class="label-text-alt"></span>
|
<span class="label-text-alt"></span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<pre>{{ form().value | json }}</pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
|
import {
|
||||||
import { FormsModule } from '@angular/forms';
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
InputSignal,
|
||||||
|
ViewChild,
|
||||||
|
input,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { DropdownComponent } from '../../../../shared/components/dropdown/dropdown.component';
|
import { DropdownComponent } from '../../../../shared/components/dropdown/dropdown.component';
|
||||||
import {
|
import {
|
||||||
|
@ -18,6 +24,7 @@ import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
DropdownComponent,
|
DropdownComponent,
|
||||||
LocationDialogComponent,
|
LocationDialogComponent,
|
||||||
|
ReactiveFormsModule,
|
||||||
],
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
|
@ -25,6 +32,7 @@ export class BasicStepComponent {
|
||||||
@ViewChild(LocationDialogComponent)
|
@ViewChild(LocationDialogComponent)
|
||||||
public locationModal!: LocationDialogComponent;
|
public locationModal!: LocationDialogComponent;
|
||||||
public items: string[] = ['Nachtigal Köln'];
|
public items: string[] = ['Nachtigal Köln'];
|
||||||
|
public form: InputSignal<FormGroup> = input.required<FormGroup>();
|
||||||
|
|
||||||
public constructor() {}
|
public constructor() {}
|
||||||
|
|
||||||
|
@ -32,8 +40,17 @@ export class BasicStepComponent {
|
||||||
this.openLocationModal();
|
this.openLocationModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getInputClass(controlName: string): string {
|
||||||
|
const control = this.form().get(controlName);
|
||||||
|
|
||||||
|
if (control?.touched) {
|
||||||
|
return control.valid ? 'input-success' : 'input-error';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
public onLocationSubmit(location: EventLocation): void {
|
public onLocationSubmit(location: EventLocation): void {
|
||||||
console.log(location);
|
//TODO: save location
|
||||||
}
|
}
|
||||||
|
|
||||||
private openLocationModal(): void {
|
private openLocationModal(): void {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="form-control w-full mb-4">
|
<label class="form-control w-full mb-4">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">{{ label() }}</span>
|
<span class="label-text">{{ label() }}</span>
|
||||||
<span class="label-text-alt">
|
<span class="label-text-alt">
|
||||||
|
@ -6,7 +6,9 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<label class="input input-bordered flex items-center gap-2">
|
<label
|
||||||
|
[ngClass]="getInputClass()"
|
||||||
|
class="input input-bordered flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
[placeholder]="placeholder()"
|
[placeholder]="placeholder()"
|
||||||
|
@ -14,6 +16,7 @@
|
||||||
(focus)="onFocus()"
|
(focus)="onFocus()"
|
||||||
(blur)="onBlur()"
|
(blur)="onBlur()"
|
||||||
(input)="onInput($event)"
|
(input)="onInput($event)"
|
||||||
|
(keyup.backspace)="onInputClear()"
|
||||||
[(ngModel)]="searchTerm" />
|
[(ngModel)]="searchTerm" />
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@ -112,4 +115,4 @@
|
||||||
<span class="label-text-alt">{{ hintBottomLeft() }}</span>
|
<span class="label-text-alt">{{ hintBottomLeft() }}</span>
|
||||||
<span class="label-text-alt">{{ hintBottomRight() }}</span>
|
<span class="label-text-alt">{{ hintBottomRight() }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</label>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
|
forwardRef,
|
||||||
input,
|
input,
|
||||||
InputSignal,
|
InputSignal,
|
||||||
output,
|
output,
|
||||||
|
@ -8,15 +9,35 @@ import {
|
||||||
signal,
|
signal,
|
||||||
WritableSignal,
|
WritableSignal,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import {
|
||||||
|
AbstractControl,
|
||||||
|
ControlValueAccessor,
|
||||||
|
FormsModule,
|
||||||
|
NG_VALIDATORS,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
ValidationErrors,
|
||||||
|
Validator,
|
||||||
|
} from '@angular/forms';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dropdown',
|
selector: 'app-dropdown',
|
||||||
templateUrl: './dropdown.component.html',
|
templateUrl: './dropdown.component.html',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule],
|
imports: [CommonModule, FormsModule],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => DropdownComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => DropdownComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class DropdownComponent {
|
export class DropdownComponent implements ControlValueAccessor, Validator {
|
||||||
public label: InputSignal<string> = input.required<string>();
|
public label: InputSignal<string> = input.required<string>();
|
||||||
public placeholder: InputSignal<string> = input.required<string>();
|
public placeholder: InputSignal<string> = input.required<string>();
|
||||||
public items: InputSignal<string[]> = input.required<string[]>();
|
public items: InputSignal<string[]> = input.required<string[]>();
|
||||||
|
@ -37,6 +58,36 @@ export class DropdownComponent {
|
||||||
public submitNewItems: OutputEmitterRef<boolean> = output<boolean>();
|
public submitNewItems: OutputEmitterRef<boolean> = output<boolean>();
|
||||||
public itemEdit: OutputEmitterRef<string> = output<string>();
|
public itemEdit: OutputEmitterRef<string> = output<string>();
|
||||||
|
|
||||||
|
public writeValue(value: string | null): void {
|
||||||
|
if (value && this.isValidOption(value)) {
|
||||||
|
this.searchTerm.set(value);
|
||||||
|
this.selectedItem.set(value);
|
||||||
|
} else {
|
||||||
|
this.searchTerm.set('');
|
||||||
|
this.selectedItem.set(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnChange(fn: (value: string) => void): void {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnTouched(fn: () => void): void {
|
||||||
|
this.onTouched = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public validate(control: AbstractControl): ValidationErrors | null {
|
||||||
|
const value = control.value;
|
||||||
|
|
||||||
|
if (this.required() && (!value || value.trim().length === 0)) {
|
||||||
|
return { required: true };
|
||||||
|
}
|
||||||
|
if (value && value.trim().length > 0 && !this.isValidOption(value)) {
|
||||||
|
return { invalidOption: true };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public onInput(event: Event): void {
|
public onInput(event: Event): void {
|
||||||
const value = (event.target as HTMLInputElement).value;
|
const value = (event.target as HTMLInputElement).value;
|
||||||
|
|
||||||
|
@ -54,13 +105,30 @@ export class DropdownComponent {
|
||||||
|
|
||||||
if (exactMatch) {
|
if (exactMatch) {
|
||||||
this.selectedItem.set(exactMatch);
|
this.selectedItem.set(exactMatch);
|
||||||
|
this.onChange(exactMatch);
|
||||||
this.itemSelected.emit(exactMatch);
|
this.itemSelected.emit(exactMatch);
|
||||||
} else {
|
} else {
|
||||||
this.selectedItem.set(null);
|
this.selectedItem.set(null);
|
||||||
|
this.onChange('');
|
||||||
this.itemSelected.emit('');
|
this.itemSelected.emit('');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showDropdown.set(true);
|
this.showDropdown.set(true);
|
||||||
|
this.onTouched();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInputClass(): string {
|
||||||
|
if (this.selectedItem()) {
|
||||||
|
return 'input-success';
|
||||||
|
} else if (
|
||||||
|
this.searchTerm().trim() !== '' &&
|
||||||
|
!this.isValidOption(this.searchTerm())
|
||||||
|
) {
|
||||||
|
return 'input-error';
|
||||||
|
} else if (this.required() && this.searchTerm().trim() === '') {
|
||||||
|
return 'input-error';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public editItem(item: string, event: MouseEvent): void {
|
public editItem(item: string, event: MouseEvent): void {
|
||||||
|
@ -71,16 +139,27 @@ export class DropdownComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public selectItem(item: string): void {
|
public selectItem(item: string): void {
|
||||||
|
if (this.isValidOption(item)) {
|
||||||
this.searchTerm.set(item);
|
this.searchTerm.set(item);
|
||||||
this.selectedItem.set(item);
|
this.selectedItem.set(item);
|
||||||
this.showDropdown.set(false);
|
this.showDropdown.set(false);
|
||||||
this.itemSelected.emit(item);
|
this.itemSelected.emit(item);
|
||||||
|
this.onChange(item);
|
||||||
|
this.onTouched();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public submitNewItem(): void {
|
public submitNewItem(): void {
|
||||||
this.submitNewItems.emit(true);
|
this.submitNewItems.emit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onInputClear(): void {
|
||||||
|
this.searchTerm.set('');
|
||||||
|
this.selectedItem.set(null);
|
||||||
|
this.onChange('');
|
||||||
|
this.onTouched();
|
||||||
|
}
|
||||||
|
|
||||||
public onFocus(): void {
|
public onFocus(): void {
|
||||||
this.showDropdown.set(true);
|
this.showDropdown.set(true);
|
||||||
this.filteredItems.set(this.items());
|
this.filteredItems.set(this.items());
|
||||||
|
@ -89,6 +168,14 @@ export class DropdownComponent {
|
||||||
public onBlur(): void {
|
public onBlur(): void {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showDropdown.set(false);
|
this.showDropdown.set(false);
|
||||||
|
this.onTouched();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isValidOption(value: string): boolean {
|
||||||
|
return this.items().includes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onChange: (value: string) => void = () => {};
|
||||||
|
private onTouched: () => void = () => {};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue