Skip to content

Commit

Permalink
Development: Use signals in video unit form component (#9692)
Browse files Browse the repository at this point in the history
  • Loading branch information
florian-glombik authored Nov 8, 2024
1 parent 25146ba commit bfebf27
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@
</div>
<div class="row">
<div class="col-12">
<button id="submitButton" class="btn btn-primary" type="submit" [disabled]="!isSubmitPossible">
<button id="submitButton" class="btn btn-primary" type="submit" [disabled]="!isFormValid()">
<span jhiTranslate="entity.action.submit"></span>
</button>
@if (hasCancelButton) {
@if (hasCancelButton()) {
<button type="button" (click)="cancelForm()" class="btn btn-default">
<fa-icon [icon]="faTimes" />&nbsp;<span jhiTranslate="entity.action.cancel"></span>
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import dayjs from 'dayjs/esm';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { Component, computed, effect, inject, input, output, untracked } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms';
import urlParser from 'js-video-url-parser';
import { faArrowLeft, faTimes } from '@fortawesome/free-solid-svg-icons';
import { CompetencyLectureUnitLink } from 'app/entities/competency.model';
import { toSignal } from '@angular/core/rxjs-interop';

export interface VideoUnitFormData {
name?: string;
Expand Down Expand Up @@ -57,30 +58,41 @@ function videoSourceUrlValidator(control: AbstractControl): ValidationErrors | u
selector: 'jhi-video-unit-form',
templateUrl: './video-unit-form.component.html',
})
export class VideoUnitFormComponent implements OnInit, OnChanges {
@Input()
formData: VideoUnitFormData;
@Input()
isEditMode = false;
export class VideoUnitFormComponent {
protected readonly faTimes = faTimes;
protected readonly faArrowLeft = faArrowLeft;

@Output()
formSubmitted: EventEmitter<VideoUnitFormData> = new EventEmitter<VideoUnitFormData>();
form: FormGroup;
private readonly formBuilder = inject(FormBuilder);

@Input()
hasCancelButton: boolean;
@Output()
onCancel: EventEmitter<any> = new EventEmitter<any>();
formData = input<VideoUnitFormData>();
isEditMode = input<boolean>(false);

faTimes = faTimes;
formSubmitted = output<VideoUnitFormData>();

hasCancelButton = input<boolean>();
onCancel = output<void>();

videoSourceUrlValidator = videoSourceUrlValidator;
videoSourceTransformUrlValidator = videoSourceTransformUrlValidator;

// Icons
faArrowLeft = faArrowLeft;

constructor(private fb: FormBuilder) {}
form: FormGroup = this.formBuilder.group({
name: [undefined as string | undefined, [Validators.required, Validators.maxLength(255)]],
description: [undefined as string | undefined, [Validators.maxLength(1000)]],
releaseDate: [undefined as dayjs.Dayjs | undefined],
source: [undefined as string | undefined, [Validators.required, this.videoSourceUrlValidator]],
urlHelper: [undefined as string | undefined, this.videoSourceTransformUrlValidator],
competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined],
});
private readonly statusChanges = toSignal(this.form.statusChanges ?? 'INVALID');
isFormValid = computed(() => this.statusChanges() === 'VALID');

constructor() {
effect(() => {
if (this.isEditMode() && this.formData()) {
untracked(() => this.setFormValues(this.formData()!));
}
});
}

get nameControl() {
return this.form.get('name');
Expand All @@ -102,31 +114,6 @@ export class VideoUnitFormComponent implements OnInit, OnChanges {
return this.form.get('urlHelper');
}

ngOnChanges(): void {
this.initializeForm();
if (this.isEditMode && this.formData) {
this.setFormValues(this.formData);
}
}

ngOnInit(): void {
this.initializeForm();
}

private initializeForm() {
if (this.form) {
return;
}
this.form = this.fb.group({
name: [undefined as string | undefined, [Validators.required, Validators.maxLength(255)]],
description: [undefined as string | undefined, [Validators.maxLength(1000)]],
releaseDate: [undefined as dayjs.Dayjs | undefined],
source: [undefined as string | undefined, [Validators.required, this.videoSourceUrlValidator]],
urlHelper: [undefined as string | undefined, this.videoSourceTransformUrlValidator],
competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined],
});
}

private setFormValues(formData: VideoUnitFormData) {
this.form.patchValue(formData);
}
Expand All @@ -136,10 +123,6 @@ export class VideoUnitFormComponent implements OnInit, OnChanges {
this.formSubmitted.emit(videoUnitFormData);
}

get isSubmitPossible() {
return !this.form.invalid;
}

get isTransformable() {
if (this.urlHelperControl!.value === undefined || this.urlHelperControl!.value === null || this.urlHelperControl!.value === '') {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { MockComponent, MockPipe } from 'ng-mocks';
import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { CompetencySelectionComponent } from 'app/shared/competency-selection/competency-selection.component';

describe('VideoUnitFormComponent', () => {
const validYouTubeUrl = 'https://www.youtube.com/watch?v=8iU8LPEa4o0';
const validYouTubeUrlInEmbeddableFormat = 'https://www.youtube.com/embed/8iU8LPEa4o0';
Expand Down Expand Up @@ -161,7 +162,7 @@ describe('VideoUnitFormComponent', () => {
});

it('should correctly set form values in edit mode', () => {
videoUnitFormComponent.isEditMode = true;
videoUnitFormComponentFixture.componentRef.setInput('isEditMode', true);
const formData: VideoUnitFormData = {
name: 'test',
description: 'lorem ipsum',
Expand All @@ -170,12 +171,18 @@ describe('VideoUnitFormComponent', () => {
};
videoUnitFormComponentFixture.detectChanges();

videoUnitFormComponent.formData = formData;
videoUnitFormComponent.ngOnChanges();
videoUnitFormComponentFixture.componentRef.setInput('formData', formData);
videoUnitFormComponentFixture.detectChanges();

expect(videoUnitFormComponent.nameControl?.value).toEqual(formData.name);
expect(videoUnitFormComponent.releaseDateControl?.value).toEqual(formData.releaseDate);
expect(videoUnitFormComponent.descriptionControl?.value).toEqual(formData.description);
expect(videoUnitFormComponent.sourceControl?.value).toEqual(formData.source);
});

it('should emit onCancel event when cancelForm is called', () => {
const onCancelSpy = jest.spyOn(videoUnitFormComponent.onCancel, 'emit');
videoUnitFormComponent.cancelForm();
expect(onCancelSpy).toHaveBeenCalled();
});
});

0 comments on commit bfebf27

Please sign in to comment.