src/app/features/attendance/deprecated/edit-legacy-attendance.component.ts

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.

Description

This component is kept for backward compatibility with the Note entity's legacy attendance format.

Extends

CustomFormControlDirective<string[]>

Implements

OnInit EditComponent

Example

Metadata

Relationships

Used by

No results matching.

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor()

Inputs

entity
Type : Note
formFieldConfig
Type : FormFieldConfig
aria-describedby
Type : string
disabled
Type : boolean
ngControl
Type : any
Default value : inject(NgControl, { optional: true, self: true })
placeholder
Type : string
required
Type : boolean
value
Type : T

Outputs

valueChange
Type : EventEmitter

Methods

getAttendance
getAttendance(childId: string)
Parameters :
Name Type Optional
childId string No
Returns : any
getStatusControl
getStatusControl(childId: string)
Parameters :
Name Type Optional
childId string No
removeChild
removeChild(id: string)
Parameters :
Name Type Optional
id string No
Returns : void
updateAttendanceValue
updateAttendanceValue(childId: unknown, property: "status" | "remarks", newValue: unknown)
Parameters :
Name Type Optional
childId unknown No
property "status" | "remarks" No
newValue unknown No
Returns : void
blur
blur()
Returns : void
focus
focus()
Returns : void
onContainerClick
onContainerClick(event: MouseEvent)
Parameters :
Name Type Optional
event MouseEvent No
Returns : void
registerOnChange
registerOnChange(fn: any)
Parameters :
Name Type Optional
fn any No
Returns : void
registerOnTouched
registerOnTouched(fn: any)
Parameters :
Name Type Optional
fn any No
Returns : void
setDescribedByIds
setDescribedByIds(ids: string[])
Parameters :
Name Type Optional
ids string[] No
Returns : void
setDisabledState
setDisabledState(isDisabled: boolean)
Parameters :
Name Type Optional
isDisabled boolean No
Returns : void
writeValue
writeValue(value: T, notifyFormControl: unknown)

Implementation for Angular ControlValueAccessor interface that links the form control value to the component value

Parameters :
Name Type Optional Default value Description
value T No

The new value to set

notifyFormControl unknown No false

Whether to notify the FormControl of this change (for internal updates)

Returns : void

Properties

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"
elementRef
Type : unknown
Default value : inject<ElementRef<HTMLElement>>(ElementRef)
Readonly enabled
Type : Signal<boolean>
Default value : computed(() => !this._disabled())

Whether the control is currently enabled, as a signal (tracks disabled).

errorStateMatcher
Type : unknown
Default value : inject(ErrorStateMatcher)
id
Type : unknown
Default value : `custom-form-control-${CustomFormControlDirective.nextId++}`
Static nextId
Type : number
Default value : 0
onChange
Type : unknown
Default value : () => {...}
onTouched
Type : unknown
Default value : () => {...}
parentForm
Type : unknown
Default value : inject(NgForm, { optional: true })
parentFormGroup
Type : unknown
Default value : inject(FormGroupDirective, { optional: true })
stateChanges
Type : unknown
Default value : new Subject<void>()
Readonly valueSignal
Type : Signal<T>
Default value : computed(() => this._value())

The current value of the control as a signal. Authoritative in both modes: it reflects the bound FormControl (synced in ngDoCheck) as well as [(value)] / writeValue updates.

Accessors

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;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""