diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/information/programming-exercise-information.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/information/programming-exercise-information.component.ts
index 78158c26e1ce..6893eef163cf 100644
--- a/src/main/webapp/app/exercises/programming/manage/update/update-components/information/programming-exercise-information.component.ts
+++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/information/programming-exercise-information.component.ts
@@ -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 &&
diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.html b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.html
index 2a895b2d195e..8176c1f6a48d 100644
--- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.html
+++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.html
@@ -73,10 +73,10 @@
-
-
+
- @if (hasCancelButton) {
+ @if (hasCancelButton()) {
diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts
index 28817538a1e4..b6d7b31447b2 100644
--- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts
+++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts
@@ -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';
@@ -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;
@@ -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 = new EventEmitter();
+ formData = input();
- @Input()
- hasCancelButton: boolean;
- @Output()
- onCancel: EventEmitter = new EventEmitter();
+ isEditMode = input(false);
+ formSubmitted = output();
- faTimes = faTimes;
+ hasCancelButton = input(false);
+ onCancel = output();
- 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');
+ isFormValid = computed(() => this.statusChanges() === 'VALID');
+
private markdownChanges = new Subject();
private markdownChangesSubscription: Subscription;
constructor(
- private fb: FormBuilder,
private router: Router,
private translateService: TranslateService,
) {}
@@ -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()!);
}
}
@@ -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) {
@@ -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);
}
diff --git a/src/main/webapp/app/lecture/lecture-update.component.ts b/src/main/webapp/app/lecture/lecture-update.component.ts
index 8e8fbd4f6dcb..b72ea717afd9 100644
--- a/src/main/webapp/app/lecture/lecture-update.component.ts
+++ b/src/main/webapp/app/lecture/lecture-update.component.ts
@@ -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;
@@ -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();
@@ -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());
diff --git a/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts b/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts
index 6fbbfdca412f..269e3a7e84e4 100644
--- a/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts
+++ b/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts
@@ -29,8 +29,14 @@ export class TitleChannelNameComponent implements AfterViewInit, OnDestroy, OnIn
@Output() titleChange = new EventEmitter();
@Output() channelNameChange = new EventEmitter();
- formValidSignal = signal(false);
+ isFormValidSignal = signal(false);
+ /**
+ * @deprecated Use {@link isFormValidSignal} instead.
+ */
formValid: boolean;
+ /**
+ * @deprecated Use {@link isFormValidSignal} instead.
+ */
formValidChanges = new Subject();
fieldTitleSubscription?: Subscription;
@@ -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);
}
diff --git a/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts
index 6c36b6f44af5..8f4411cd4f40 100644
--- a/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts
+++ b/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts
@@ -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',
@@ -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);
@@ -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
diff --git a/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts
index 4b9fcac77a25..fae301e74331 100644
--- a/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts
+++ b/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts
@@ -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);