src/app/features/attendance/deprecated/edit-legacy-attendance.component.ts
Use the new {@link EditAttendanceComponent} with the attendance datatype instead.
This component is kept for backward compatibility with the Note entity's legacy attendance format.
This component is kept for backward compatibility with the Note entity's legacy attendance format.
CustomFormControlDirective<string[]>
| changeDetection | ChangeDetectionStrategy.OnPush |
| providers |
EditLegacyAttendanceComponent
|
| selector | app-edit-legacy-attendance |
| standalone | true |
| imports |
FontAwesomeModule
MatButtonModule
MatInputModule
MatCardModule
|
| styleUrls | ./edit-legacy-attendance.component.scss |
| templateUrl | ./edit-legacy-attendance.component.html |
No results matching.
ReactiveFormsModule
EditEntityComponent
FontAwesomeModule
EntityBlockComponent
MatButtonModule
EditConfigurableEnumComponent
MatInputModule
MatCardModule
CustomFormControlDirective<string[]>
OnInit
EditComponent
Properties |
|
Methods |
Inputs |
Outputs |
constructor()
|
| entity | |
Type : Note
|
|
| formFieldConfig | |
Type : FormFieldConfig
|
|
| aria-describedby | |
Type : string
|
|
|
Inherited from
CustomFormControlDirective
|
|
| disabled | |
Type : boolean
|
|
|
Inherited from
CustomFormControlDirective
|
|
| ngControl | |
Type : any
|
|
Default value : inject(NgControl, { optional: true, self: true })
|
|
|
Inherited from
CustomFormControlDirective
|
|
| placeholder | |
Type : string
|
|
|
Inherited from
CustomFormControlDirective
|
|
| required | |
Type : boolean
|
|
|
Inherited from
CustomFormControlDirective
|
|
| value | |
Type : T
|
|
|
Inherited from
CustomFormControlDirective
|
|
| valueChange | |
Type : EventEmitter
|
|
|
Inherited from
CustomFormControlDirective
|
|
| getAttendance | ||||||
getAttendance(childId: string)
|
||||||
|
Parameters :
Returns :
any
|
| getStatusControl | ||||||
getStatusControl(childId: string)
|
||||||
|
Parameters :
Returns :
FormControl<ConfigurableEnumValue>
|
| removeChild | ||||||
removeChild(id: string)
|
||||||
|
Parameters :
Returns :
void
|
| updateAttendanceValue | ||||||||||||
updateAttendanceValue(childId: unknown, property: "status" | "remarks", newValue: unknown)
|
||||||||||||
|
Parameters :
Returns :
void
|
| blur |
blur()
|
|
Inherited from
CustomFormControlDirective
|
|
Returns :
void
|
| focus |
focus()
|
|
Inherited from
CustomFormControlDirective
|
|
Returns :
void
|
| onContainerClick | ||||||
onContainerClick(event: MouseEvent)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| registerOnChange | ||||||
registerOnChange(fn: any)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| registerOnTouched | ||||||
registerOnTouched(fn: any)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| setDescribedByIds | ||||||
setDescribedByIds(ids: string[])
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| setDisabledState | ||||||
setDisabledState(isDisabled: boolean)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| writeValue | |||||||||||||||
writeValue(value: T, notifyFormControl: unknown)
|
|||||||||||||||
|
Inherited from
CustomFormControlDirective
|
|||||||||||||||
|
Implementation for Angular ControlValueAccessor interface that links the form control value to the component value
Parameters :
Returns :
void
|
| mobile |
Type : unknown
|
Default value : signal(false)
|
| showAttendance |
Type : unknown
|
Default value : signal(false)
|
| Readonly statusFieldConfig |
Type : FormFieldConfig
|
Default value : {
id: "status",
dataType: "configurable-enum",
additional: ATTENDANCE_STATUS_CONFIG_ID,
}
|
| controlType |
Type : string
|
Default value : "custom-control"
|
|
Inherited from
CustomFormControlDirective
|
| elementRef |
Type : unknown
|
Default value : inject<ElementRef<HTMLElement>>(ElementRef)
|
|
Inherited from
CustomFormControlDirective
|
| Readonly enabled |
Type : Signal<boolean>
|
Default value : computed(() => !this._disabled())
|
|
Inherited from
CustomFormControlDirective
|
|
Whether the control is currently enabled, as a signal (tracks disabled). |
| errorStateMatcher |
Type : unknown
|
Default value : inject(ErrorStateMatcher)
|
|
Inherited from
CustomFormControlDirective
|
| id |
Type : unknown
|
Default value : `custom-form-control-${CustomFormControlDirective.nextId++}`
|
|
Inherited from
CustomFormControlDirective
|
| Static nextId |
Type : number
|
Default value : 0
|
|
Inherited from
CustomFormControlDirective
|
| onChange |
Type : unknown
|
Default value : () => {...}
|
|
Inherited from
CustomFormControlDirective
|
| onTouched |
Type : unknown
|
Default value : () => {...}
|
|
Inherited from
CustomFormControlDirective
|
| parentForm |
Type : unknown
|
Default value : inject(NgForm, { optional: true })
|
|
Inherited from
CustomFormControlDirective
|
| parentFormGroup |
Type : unknown
|
Default value : inject(FormGroupDirective, { optional: true })
|
|
Inherited from
CustomFormControlDirective
|
| stateChanges |
Type : unknown
|
Default value : new Subject<void>()
|
|
Inherited from
CustomFormControlDirective
|
| Readonly valueSignal |
Type : Signal<T>
|
Default value : computed(() => this._value())
|
|
Inherited from
CustomFormControlDirective
|
|
The current value of the control as a signal.
Authoritative in both modes: it reflects the bound |
import {
Component,
inject,
input,
OnInit,
ChangeDetectionStrategy,
signal,
} from "@angular/core";
import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms";
import { MatButtonModule } from "@angular/material/button";
import { MatCardModule } from "@angular/material/card";
import { MatFormFieldControl } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
import { startWith } from "rxjs/operators";
import { EditEntityComponent } from "#src/app/core/basic-datatypes/entity/edit-entity/edit-entity.component";
import { EntityBlockComponent } from "#src/app/core/basic-datatypes/entity/entity-block/entity-block.component";
import { CustomFormControlDirective } from "#src/app/core/common-components/basic-autocomplete/custom-form-control.directive";
import { FormFieldConfig } from "#src/app/core/common-components/entity-form/FormConfig";
import { DynamicComponent } from "#src/app/core/config/dynamic-components/dynamic-component.decorator";
import { EditComponent } from "#src/app/core/entity/entity-field-edit/dynamic-edit/edit-component.interface";
import { ScreenWidthObserver } from "#src/app/utils/media/screen-size-observer.service";
import { InteractionType } from "#src/app/child-dev-project/notes/model/interaction-type.interface";
import { Note } from "#src/app/child-dev-project/notes/model/note";
import { EditConfigurableEnumComponent } from "#src/app/core/basic-datatypes/configurable-enum/edit-configurable-enum/edit-configurable-enum.component";
import { ConfigurableEnumValue } from "#src/app/core/basic-datatypes/configurable-enum/configurable-enum.types";
import {
ATTENDANCE_STATUS_CONFIG_ID,
AttendanceStatusType,
} from "../model/attendance-status";
import { AttendanceItem } from "../model/attendance-item";
/**
* @deprecated Use the new {@link EditAttendanceComponent} with the `attendance` datatype instead.
* This component is kept for backward compatibility with the Note entity's legacy attendance format.
*/
@UntilDestroy()
@DynamicComponent("EditLegacyAttendance")
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "app-edit-legacy-attendance",
imports: [
ReactiveFormsModule,
EditEntityComponent,
FontAwesomeModule,
EntityBlockComponent,
MatButtonModule,
EditConfigurableEnumComponent,
MatInputModule,
MatCardModule,
],
templateUrl: "./edit-legacy-attendance.component.html",
styleUrls: ["./edit-legacy-attendance.component.scss"],
providers: [
{
provide: MatFormFieldControl,
useExisting: EditLegacyAttendanceComponent,
},
],
})
export class EditLegacyAttendanceComponent
extends CustomFormControlDirective<string[]>
implements OnInit, EditComponent
{
formFieldConfig = input<FormFieldConfig>();
entity = input<Note>();
showAttendance = signal(false);
mobile = signal(false);
readonly statusFieldConfig: FormFieldConfig = {
id: "status",
dataType: "configurable-enum",
additional: ATTENDANCE_STATUS_CONFIG_ID,
};
private readonly statusControls = new Map<
string,
FormControl<ConfigurableEnumValue>
>();
getStatusControl(childId: string): FormControl<ConfigurableEnumValue> {
let ctrl = this.statusControls.get(childId);
if (!ctrl) {
ctrl = new FormControl<ConfigurableEnumValue>(
this.getAttendance(childId).status ?? null,
);
ctrl.valueChanges.pipe(untilDestroyed(this)).subscribe((value) => {
this.updateAttendanceValue(
childId,
"status",
value as AttendanceStatusType,
);
});
this.statusControls.set(childId, ctrl);
}
return ctrl;
}
get parent() {
return this.formControl.parent as FormGroup;
}
constructor() {
super();
const screenWithObserver = inject(ScreenWidthObserver);
screenWithObserver
.platform()
.pipe(untilDestroyed(this))
.subscribe((isDesktop) => this.mobile.set(!isDesktop));
}
ngOnInit() {
const category = this.parent.get(
"category",
) as FormControl<InteractionType>;
if (category) {
category.valueChanges
.pipe(startWith(category.value), untilDestroyed(this))
.subscribe((val) => {
this.showAttendance.set(!!val?.isMeeting);
if (this.showAttendance()) {
let childrenAttendanceForm = new FormControl(
this.entity()?.copy()?.["childrenAttendance"],
);
this.parent.addControl(
"childrenAttendance",
childrenAttendanceForm,
);
} else {
this.parent.removeControl("childrenAttendance");
}
});
}
}
getAttendance(childId: string) {
const attendanceList: AttendanceItem[] =
this.parent.get("childrenAttendance").value;
let attendance = attendanceList.find(
(item) => item.participant === childId,
);
if (!attendance) {
attendance = new AttendanceItem();
attendance.participant = childId;
attendanceList.push(attendance);
}
return attendance;
}
removeChild(id: string) {
const children = this.formControl.value;
const index = children.indexOf(id);
if (index < 0) {
return;
}
children.splice(index, 1);
const attendanceList: AttendanceItem[] =
this.parent.get("childrenAttendance").value;
const attIndex = attendanceList.findIndex(
(item) => item.participant === id,
);
if (attIndex >= 0) {
attendanceList.splice(attIndex, 1);
}
this.formControl.markAsDirty();
this.formControl.setValue([...children]);
}
updateAttendanceValue(childId, property: "status" | "remarks", newValue) {
this.formControl.markAsDirty();
this.getAttendance(childId)[property] = newValue;
}
}
<app-edit-entity
[formControl]="formControl"
[formFieldConfig]="formFieldConfig()"
[showEntities]="!showAttendance()"
></app-edit-entity>
@if (showAttendance()) {
<!-- If feasible, this whole setup should be replaced with a more simple setup that
automatically adapts to the screen size without having to rely on two different layout techniques for
small and big screens.
-->
@if (!mobile()) {
<div>
<!-- Desktop view: display the information as table -->
<table class="table">
@for (childId of formControl.value; track childId) {
<tr>
<td>
@if (!formControl.disabled) {
<button mat-icon-button (click)="removeChild(childId)">
<fa-icon icon="trash"></fa-icon>
</button>
}
</td>
<td>
<app-entity-block [entityId]="childId"></app-entity-block>
</td>
<td>
<mat-form-field class="margin-small">
<app-edit-configurable-enum
[formControl]="getStatusControl(childId)"
[formFieldConfig]="statusFieldConfig"
></app-edit-configurable-enum>
</mat-form-field>
</td>
<td class="full-width">
<mat-form-field class="adjust-top margin-small">
<input
#inputElement
matInput
i18n-placeholder
placeholder="Remarks"
name="remarks"
type="text"
[value]="getAttendance(childId).remarks"
(input)="
updateAttendanceValue(
childId,
'remarks',
inputElement.value
)
"
[disabled]="formControl.disabled"
/>
</mat-form-field>
</td>
</tr>
}
</table>
</div>
} @else {
<div class="attendance-blocks">
<!-- Mobile view / smaller screen: display the information using a flex-layout -->
@for (childId of formControl.value; track childId) {
<mat-card class="attendance-item mat-elevation-z1 margin-bottom-small">
<mat-card-content>
<div class="attendance-item--header margin-bottom-regular">
<app-entity-block [entityId]="childId"></app-entity-block>
@if (!formControl.disabled) {
<button
mat-icon-button
(click)="removeChild(childId)"
class="mobile-remove-item"
>
<fa-icon icon="trash"></fa-icon>
</button>
}
</div>
<div class="attendance-item--content">
<mat-form-field class="margin-small">
<app-edit-configurable-enum
[formControl]="getStatusControl(childId)"
[formFieldConfig]="statusFieldConfig"
></app-edit-configurable-enum>
</mat-form-field>
<mat-form-field>
<input
#inputElement
matInput
i18n-placeholder
placeholder="Remarks"
name="remarks"
type="text"
[value]="getAttendance(childId).remarks"
(input)="
updateAttendanceValue(
childId,
'remarks',
inputElement.value
)
"
[disabled]="formControl.disabled"
/>
</mat-form-field>
</div>
</mat-card-content>
</mat-card>
}
</div>
}
}
./edit-legacy-attendance.component.scss
/* make layout of attendee items more dense and align its fields by hiding unused subscript space */
:host ::ng-deep .table .mat-mdc-form-field-subscript-wrapper {
display: none;
}
.table {
/* add gap of same size as the hidden subscript wrappers */
margin-bottom: 22px;
}
.mobile-remove-item {
position: absolute;
top: 0;
right: 0;
}