Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Use signals in lecture online and text unit #9658

Merged
merged 9 commits into from
Nov 7, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export class ProgrammingExerciseInformationComponent implements AfterViewInit, O
const areAuxiliaryRepositoriesValid = this.areAuxiliaryRepositoriesValid();
const areCheckoutPathsValid = this.areCheckoutPathsValid();
this.formValid = Boolean(
this.exerciseTitleChannelComponent()?.titleChannelNameComponent?.formValidSignal() &&
this.exerciseTitleChannelComponent()?.titleChannelNameComponent?.isFormValidSignal() &&
this.getIsShortNameFieldValid() &&
isCheckoutSolutionRepositoryValid &&
isRecreateBuildPlansValid &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,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>
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved
@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,12 +1,13 @@
import dayjs from 'dayjs/esm';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { Component, OnChanges, computed, inject, input, output } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { faArrowLeft, faTimes } from '@fortawesome/free-solid-svg-icons';
import { map } from 'rxjs';
import { HttpResponse } from '@angular/common/http';
import { OnlineResourceDTO } from 'app/lecture/lecture-unit/lecture-unit-management/online-resource-dto.model';
import { OnlineUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/onlineUnit.service';
import { CompetencyLectureUnitLink } from 'app/entities/competency.model';
import { toSignal } from '@angular/core/rxjs-interop';

export interface OnlineUnitFormData {
name?: string;
Expand All @@ -32,32 +33,33 @@ function urlValidator(control: AbstractControl) {
selector: 'jhi-online-unit-form',
templateUrl: './online-unit-form.component.html',
})
export class OnlineUnitFormComponent implements OnInit, OnChanges {
@Input()
formData: OnlineUnitFormData;
@Input()
isEditMode = false;
export class OnlineUnitFormComponent implements OnChanges {
protected readonly faArrowLeft = faArrowLeft;
protected readonly faTimes = faTimes;

@Output()
formSubmitted: EventEmitter<OnlineUnitFormData> = new EventEmitter<OnlineUnitFormData>();
form: FormGroup;
formData = input<OnlineUnitFormData>();
isEditMode = input<boolean>(false);

@Input()
hasCancelButton: boolean;
@Output()
onCancel: EventEmitter<any> = new EventEmitter<any>();
formSubmitted = output<OnlineUnitFormData>();

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

urlValidator = urlValidator;

// Icons
faArrowLeft = faArrowLeft;
private readonly formBuilder = inject(FormBuilder);
private readonly onlineUnitService = inject(OnlineUnitService);

constructor(
private fb: FormBuilder,
private onlineUnitService: OnlineUnitService,
) {}
form: FormGroup = this.formBuilder.group({
name: [undefined, [Validators.required, Validators.maxLength(255)]],
description: [undefined, [Validators.maxLength(1000)]],
releaseDate: [undefined],
source: [undefined, [Validators.required, this.urlValidator]],
competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined],
});

private readonly statusChanges = toSignal(this.form.statusChanges ?? 'INVALID');
isFormValid = computed(() => this.statusChanges() === 'VALID');

get nameControl() {
return this.form.get('name');
Expand All @@ -76,27 +78,9 @@ export class OnlineUnitFormComponent implements OnInit, OnChanges {
}

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

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

private initializeForm() {
if (this.form) {
return;
if (this.isEditMode() && this.formData()) {
this.setFormValues(this.formData()!);
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved
}
this.form = this.fb.group({
name: [undefined, [Validators.required, Validators.maxLength(255)]],
description: [undefined, [Validators.maxLength(1000)]],
releaseDate: [undefined],
source: [undefined, [Validators.required, this.urlValidator]],
competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined],
});
}

private setFormValues(formData: OnlineUnitFormData) {
Expand Down Expand Up @@ -136,10 +120,6 @@ export class OnlineUnitFormComponent implements OnInit, OnChanges {
this.formSubmitted.emit(onlineUnitFormData);
}

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

cancelForm() {
this.onCancel.emit();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,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()">
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved
<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,4 +1,4 @@
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
import { Component, OnChanges, OnDestroy, OnInit, computed, inject, input, output } from '@angular/core';
import dayjs from 'dayjs/esm';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
Expand All @@ -7,6 +7,7 @@ import { debounceTime } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { CompetencyLectureUnitLink } from 'app/entities/competency.model';
import { toSignal } from '@angular/core/rxjs-interop';

export interface TextUnitFormData {
name?: string;
Expand All @@ -21,30 +22,36 @@ export interface TextUnitFormData {
styles: [],
})
export class TextUnitFormComponent implements OnInit, OnChanges, OnDestroy {
@Input()
formData: TextUnitFormData;
protected readonly faTimes = faTimes;

@Input() isEditMode = false;
@Output() formSubmitted: EventEmitter<TextUnitFormData> = new EventEmitter<TextUnitFormData>();
formData = input<TextUnitFormData>();

@Input()
hasCancelButton: boolean;
@Output()
onCancel: EventEmitter<any> = new EventEmitter<any>();
isEditMode = input<boolean>(false);
formSubmitted = output<TextUnitFormData>();

faTimes = faTimes;
hasCancelButton = input<boolean>(false);
onCancel = output<void>();
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved

form: FormGroup;
// not included in reactive form
content: string | undefined;
contentLoadedFromCache = false;
firstMarkdownChangeHappened = false;

private readonly formBuilder = inject(FormBuilder);

form: FormGroup = this.formBuilder.group({
name: [undefined as string | undefined, [Validators.required, Validators.maxLength(255)]],
releaseDate: [undefined as dayjs.Dayjs | undefined],
competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined],
});

private readonly statusChanges = toSignal(this.form.statusChanges ?? 'INVALID');
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved
isFormValid = computed(() => this.statusChanges() === 'VALID');
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved

private markdownChanges = new Subject<string>();
private markdownChangesSubscription: Subscription;

constructor(
private fb: FormBuilder,
private router: Router,
private translateService: TranslateService,
) {}
Expand All @@ -58,9 +65,8 @@ export class TextUnitFormComponent implements OnInit, OnChanges, OnDestroy {
}

ngOnChanges(): void {
this.initializeForm();
if (this.isEditMode && this.formData) {
this.setFormValues(this.formData);
if (this.isEditMode() && this.formData()) {
this.setFormValues(this.formData()!);
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -85,18 +91,6 @@ export class TextUnitFormComponent implements OnInit, OnChanges, OnDestroy {
this.firstMarkdownChangeHappened = true;
}
});
this.initializeForm();
}

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

private setFormValues(formData: TextUnitFormData) {
Expand All @@ -115,10 +109,6 @@ export class TextUnitFormComponent implements OnInit, OnChanges, OnDestroy {
this.formSubmitted.emit(textUnitFormData);
}

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

onMarkdownChange(markdown: string) {
this.markdownChanges.next(markdown);
}
Expand Down
27 changes: 12 additions & 15 deletions src/main/webapp/app/lecture/lecture-update.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,16 @@ import { FormulaAction } from 'app/shared/monaco-editor/model/actions/formula.ac
styleUrls: ['./lecture-update.component.scss'],
})
export class LectureUpdateComponent implements OnInit {
readonly documentationType: DocumentationType = 'Lecture';
protected readonly documentationType: DocumentationType = 'Lecture';
protected readonly faQuestionCircle = faQuestionCircle;
protected readonly faSave = faSave;
protected readonly faPuzzleProcess = faPuzzlePiece;
protected readonly faBan = faBan;
protected readonly faHandShakeAngle = faHandshakeAngle;
// A human-readable list of allowed file extensions
protected readonly allowedFileExtensions = UPLOAD_FILE_EXTENSIONS.join(', ');
// The list of file extensions for the "accept" attribute of the file input field
protected readonly acceptedFileExtensionsFileBrowser = UPLOAD_FILE_EXTENSIONS.map((ext) => '.' + ext).join(',');

@ViewChild(LectureUpdateWizardComponent, { static: false }) wizardComponent: LectureUpdateWizardComponent;

Expand All @@ -38,18 +47,6 @@ export class LectureUpdateComponent implements OnInit {
fileName: string;
fileInputTouched = false;

// Icons
faQuestionCircle = faQuestionCircle;
faSave = faSave;
faPuzzleProcess = faPuzzlePiece;
faBan = faBan;
faHandShakeAngle = faHandshakeAngle;

// A human-readable list of allowed file extensions
readonly allowedFileExtensions = UPLOAD_FILE_EXTENSIONS.join(', ');
// The list of file extensions for the "accept" attribute of the file input field
readonly acceptedFileExtensionsFileBrowser = UPLOAD_FILE_EXTENSIONS.map((ext) => '.' + ext).join(',');

toggleModeFunction = () => this.toggleWizardMode();
saveLectureFunction = () => this.save();

Expand Down Expand Up @@ -89,8 +86,8 @@ export class LectureUpdateComponent implements OnInit {

/**
* Revert to the previous state, equivalent with pressing the back button on your browser
* Returns to the detail page if there is no previous state and we edited an existing lecture
* Returns to the overview page if there is no previous state and we created a new lecture
* Returns to the detail page if there is no previous state, and we edited an existing lecture
* Returns to the overview page if there is no previous state, and we created a new lecture
*/
previousState() {
this.navigationUtilService.navigateBackWithOptional(['course-management', this.lecture.course!.id!.toString(), 'lectures'], this.lecture.id?.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ export class TitleChannelNameComponent implements AfterViewInit, OnDestroy, OnIn
@Output() titleChange = new EventEmitter<string>();
@Output() channelNameChange = new EventEmitter<string>();

formValidSignal = signal<boolean>(false);
isFormValidSignal = signal<boolean>(false);
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved
/**
* @deprecated Use {@link isFormValidSignal} instead.
*/
formValid: boolean;
/**
* @deprecated Use {@link isFormValidSignal} instead.
*/
formValidChanges = new Subject();

fieldTitleSubscription?: Subscription;
Expand Down Expand Up @@ -88,7 +94,7 @@ export class TitleChannelNameComponent implements AfterViewInit, OnDestroy, OnIn

calculateFormValid(): void {
const updatedFormValidValue = Boolean(this.field_title.valid && (!this.isChannelFieldDisplayed() || this.field_channel_name()?.valid));
this.formValidSignal.set(updatedFormValidValue);
this.isFormValidSignal.set(updatedFormValidValue);
this.formValid = updatedFormValidValue;
this.formValidChanges.next(this.formValid);
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe('OnlineUnitFormComponent', () => {
});

it('should correctly set form values in edit mode', () => {
onlineUnitFormComponent.isEditMode = true;
onlineUnitFormComponentFixture.componentRef.setInput('isEditMode', true);
const formData: OnlineUnitFormData = {
name: 'test',
description: 'lorem ipsum',
Expand All @@ -130,7 +130,7 @@ describe('OnlineUnitFormComponent', () => {
};
onlineUnitFormComponentFixture.detectChanges();

onlineUnitFormComponent.formData = formData;
onlineUnitFormComponentFixture.componentRef.setInput('formData', formData);
onlineUnitFormComponent.ngOnChanges();

expect(onlineUnitFormComponent.nameControl?.value).toEqual(formData.name);
Expand All @@ -151,10 +151,10 @@ describe('OnlineUnitFormComponent', () => {
const getOnlineResourceStub = jest.spyOn(onlineUnitService, 'getOnlineResource').mockReturnValue(of(response));

onlineUnitFormComponentFixture.detectChanges();
onlineUnitFormComponent.isEditMode = true;
onlineUnitFormComponent.formData = {
onlineUnitFormComponentFixture.componentRef.setInput('isEditMode', true);
onlineUnitFormComponentFixture.componentRef.setInput('formData', {
source: 'example.com',
};
});
onlineUnitFormComponent.ngOnChanges();

// WHEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,11 @@ describe('TextUnitFormComponent', () => {

// init
textUnitFormComponentFixture.detectChanges(); // ngOnInit
textUnitFormComponent.isEditMode = true;
textUnitFormComponentFixture.componentRef.setInput('isEditMode', true);
tick();

// setting the form data
textUnitFormComponent.formData = formData;
textUnitFormComponent.ngOnChanges(); // ngOnChanges
textUnitFormComponentFixture.componentRef.setInput('formData', formData);
textUnitFormComponent.ngOnChanges();
textUnitFormComponentFixture.detectChanges();

expect(textUnitFormComponent.nameControl!.value).toEqual(formData.name);
Expand Down
Loading