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,7 +73,7 @@
</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) {
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, EventEmitter, Input, OnChanges, Output, computed, inject } 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;
@Input() formData: OnlineUnitFormData;
@Input() isEditMode = false;

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

faTimes = faTimes;
@Input() hasCancelButton: boolean;
@Output() onCancel: EventEmitter<any> = new EventEmitter<any>();
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved

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');
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved

get nameControl() {
return this.form.get('name');
Expand All @@ -76,29 +78,11 @@ 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;
}
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) {
this.form.patchValue(formData);
}
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,7 +50,7 @@
</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) {
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, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, computed, inject } 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() formData: TextUnitFormData;

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

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

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

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');
isFormValid = computed(() => this.statusChanges() === 'VALID');
florian-glombik marked this conversation as resolved.
Show resolved Hide resolved

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,7 +65,6 @@ export class TextUnitFormComponent implements OnInit, OnChanges, OnDestroy {
}

ngOnChanges(): void {
this.initializeForm();
if (this.isEditMode && this.formData) {
this.setFormValues(this.formData);
}
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
Loading