feat: add a retry button in case of a load error

This commit is contained in:
Polianin Nikita 2024-03-13 04:35:27 +03:00
parent 4463d54cfb
commit 2f280b2b95
10 changed files with 107 additions and 59 deletions

View File

@ -0,0 +1,7 @@
@if (loading) {
<app-data-spinner/>
} @else {
<button mat-fab color="primary" (click)="retryFunction.emit()">
<mat-icon>refresh</mat-icon>
</button>
}

View File

@ -0,0 +1,20 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component";
import {MatIcon} from "@angular/material/icon";
import {MatButton, MatFabButton} from "@angular/material/button";
@Component({
selector: 'app-loading-indicator',
standalone: true,
imports: [
DataSpinnerComponent,
MatButton,
MatIcon,
MatFabButton
],
templateUrl: './loading-indicator.component.html'
})
export class LoadingIndicatorComponent {
@Input() loading: boolean = true;
@Output() retryFunction: EventEmitter<void> = new EventEmitter<void>();
}

View File

@ -5,30 +5,31 @@
Факультет Факультет
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-chip-listbox hideSingleSelectionIndicator (change)="selectedFaculty($event)"> <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseFaculty($event)">
@for (faculty of faculties | async; track $index) { @for (faculty of faculties | async; track $index) {
<mat-chip-option [value]="faculty.id" color="accent"> <mat-chip-option [value]="faculty.id" color="accent">
{{ faculty.name }} {{ faculty.name }}
</mat-chip-option> </mat-chip-option>
} @empty { } @empty {
<app-data-spinner/> <app-loading-indicator [loading]="facultiesLoaded !== null" (retryFunction)="facultiesLoadRetry.emit()"/>
} }
</mat-chip-listbox> </mat-chip-listbox>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel [disabled]="facultiesId === null" #courseNumberPanel> <mat-expansion-panel [disabled]="facultyId === null" #courseNumberPanel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>
Курс Курс
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-chip-listbox hideSingleSelectionIndicator (change)="selectCourseNumber($event)" [formControl]="chipCourse"> <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseCourseNumber($event)" [formControl]="chipCourse">
@for (course of courseNumbers | async; track $index) { @for (course of courseNumbers | async; track $index) {
<mat-chip-option [value]="course" color="accent"> <mat-chip-option [value]="course" color="accent">
{{ course }} {{ course }}
</mat-chip-option> </mat-chip-option>
} @empty { } @empty {
<app-data-spinner/> <app-loading-indicator [loading]="groupsLoaded !== null"
(retryFunction)="groupsLoadRetry.emit(this.facultyId!)"/>
} }
</mat-chip-listbox> </mat-chip-listbox>
</mat-expansion-panel> </mat-expansion-panel>
@ -39,13 +40,14 @@
Группа Группа
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-chip-listbox hideSingleSelectionIndicator (change)="selectGroup($event)" [formControl]="chipGroup"> <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseGroup($event)" [formControl]="chipGroup">
@for (group of filteredGroups | async; track $index) { @for (group of filteredGroups | async; track $index) {
<mat-chip-option [value]="group.id" color="accent"> <mat-chip-option [value]="group.id" color="accent">
{{ group.name }} {{ group.name }}
</mat-chip-option> </mat-chip-option>
} @empty { } @empty {
<app-data-spinner/> <app-loading-indicator [loading]="groupsLoaded !== null"
(retryFunction)="groupsLoadRetry.emit(this.facultyId!)"/>
} }
</mat-chip-listbox> </mat-chip-listbox>
</mat-expansion-panel> </mat-expansion-panel>

View File

@ -1,29 +1,31 @@
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'; import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
import {MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion"; import {MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component";
import {MatChipListboxChange, MatChipsModule} from '@angular/material/chips'; import {MatChipListboxChange, MatChipsModule} from '@angular/material/chips';
import {FormControl, ReactiveFormsModule} from "@angular/forms"; import {FormControl, ReactiveFormsModule} from "@angular/forms";
import {AsyncPipe} from "@angular/common"; import {AsyncPipe} from "@angular/common";
import {map, Observable, of} from "rxjs"; import {map, Observable, of} from "rxjs";
import {FacultyResponse} from "@model/facultyResponse"; import {FacultyResponse} from "@model/facultyResponse";
import {GroupResponse} from "@model/groupResponse"; import {GroupResponse} from "@model/groupResponse";
import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component";
@Component({ @Component({
selector: 'app-schedule-tabs-group', selector: 'app-schedule-tabs-group',
standalone: true, standalone: true,
imports: [ imports: [
MatExpansionModule, MatExpansionModule,
DataSpinnerComponent,
MatChipsModule, MatChipsModule,
ReactiveFormsModule, ReactiveFormsModule,
LoadingIndicatorComponent,
AsyncPipe AsyncPipe
], ],
templateUrl: './schedule-tabs-group.component.html', templateUrl: './schedule-tabs-group.component.html',
styleUrl: './schedule-tabs-group.component.css' styleUrl: './schedule-tabs-group.component.css'
}) })
export class ScheduleTabsGroupComponent { export class ScheduleTabsGroupComponent {
protected facultiesId: number | null = null; protected facultyId: number | null = null;
protected courseNumber: number | null = null; protected courseNumber: number | null = null;
protected filteredGroups: Observable<GroupResponse[]> = of([]); protected filteredGroups: Observable<GroupResponse[]> = of([]);
protected courseNumbers: Observable<number[]> = of([]); protected courseNumbers: Observable<number[]> = of([]);
protected groups: Observable<GroupResponse[]> = of([]); protected groups: Observable<GroupResponse[]> = of([]);
@ -35,8 +37,12 @@ export class ScheduleTabsGroupComponent {
@ViewChild('groupPanel') groupPanel!: MatExpansionPanel; @ViewChild('groupPanel') groupPanel!: MatExpansionPanel;
@Input() faculties: Observable<FacultyResponse[]> = of([]); @Input() faculties: Observable<FacultyResponse[]> = of([]);
@Input() facultiesLoaded: boolean | null = false;
@Output() facultiesLoadRetry: EventEmitter<void> = new EventEmitter<void>();
@Input() groupsLoaded: boolean | null = false;
@Output() groupsLoadRetry: EventEmitter<number> = new EventEmitter<number>();
@Input() set groupsSet(data: Observable<GroupResponse[]>) { @Input() set setGroups(data: Observable<GroupResponse[]>) {
this.groups = data; this.groups = data;
this.courseNumbers = this.groups.pipe( this.courseNumbers = this.groups.pipe(
map(data => data.map(g => g.courseNumber)), map(data => data.map(g => g.courseNumber)),
@ -48,23 +54,23 @@ export class ScheduleTabsGroupComponent {
@Output() groupSelected = new EventEmitter<number>(); @Output() groupSelected = new EventEmitter<number>();
@Output() facultySelected = new EventEmitter<number>(); @Output() facultySelected = new EventEmitter<number>();
protected selectedFaculty(event: MatChipListboxChange) { protected chooseFaculty(event: MatChipListboxChange) {
this.courseNumber = null; this.courseNumber = null;
this.groups = of([]); this.groups = of([]);
this.chipGroup.reset(); this.chipGroup.reset();
this.chipCourse.reset(); this.chipCourse.reset();
if (event.value === undefined || event.value === null) { if (event.value === undefined || event.value === null) {
this.facultiesId = null; this.facultyId = null;
return; return;
} }
this.facultiesId = event.value; this.facultyId = event.value;
this.courseNumberPanel.open(); this.courseNumberPanel.open();
this.facultySelected.emit(this.facultiesId!); this.facultySelected.emit(this.facultyId!);
} }
protected selectCourseNumber(event: MatChipListboxChange) { protected chooseCourseNumber(event: MatChipListboxChange) {
this.filteredGroups = of([]); this.filteredGroups = of([]);
this.chipGroup.reset(); this.chipGroup.reset();
@ -79,7 +85,7 @@ export class ScheduleTabsGroupComponent {
this.filteredGroups = of(data.filter(g => g.courseNumber === this.courseNumber))); this.filteredGroups = of(data.filter(g => g.courseNumber === this.courseNumber)));
} }
protected selectGroup(event: MatChipListboxChange) { protected chooseGroup(event: MatChipListboxChange) {
if (event.value === undefined || event.value === null) if (event.value === undefined || event.value === null)
return; return;

View File

@ -5,13 +5,13 @@
Кампус Кампус
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-chip-listbox hideSingleSelectionIndicator (change)="selectedCampus($event)"> <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseCampus($event)">
@for (campus of campuses | async; track $index) { @for (campus of campuses | async; track $index) {
<mat-chip-option [value]="campus.id" color="accent"> <mat-chip-option [value]="campus.id" color="accent">
{{ campus.codeName }} {{ campus.codeName }}
</mat-chip-option> </mat-chip-option>
} @empty { } @empty {
<app-data-spinner/> <app-loading-indicator [loading]="campusesLoaded !== null" (retryFunction)="campusesLoadRetry.emit()"/>
} }
</mat-chip-listbox> </mat-chip-listbox>
</mat-expansion-panel> </mat-expansion-panel>
@ -22,13 +22,13 @@
Кабинет Кабинет
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<mat-chip-listbox hideSingleSelectionIndicator (change)="selectedLectureHall($event)" [formControl]="chipLecture"> <mat-chip-listbox hideSingleSelectionIndicator (change)="chooseLectureHall($event)" [formControl]="chipLecture">
@for (lectureHall of lectureHalls | async; track $index) { @for (lectureHall of lectureHalls | async; track $index) {
<mat-chip-option [value]="lectureHall.id" color="accent"> <mat-chip-option [value]="lectureHall.id" color="accent">
{{ lectureHall.name }} {{ lectureHall.name }}
</mat-chip-option> </mat-chip-option>
} @empty { } @empty {
<app-data-spinner/> <app-loading-indicator [loading]="lectureHallsLoaded !== null" (retryFunction)="lectureHallsLoadRetry.emit()"/>
} }
</mat-chip-listbox> </mat-chip-listbox>
</mat-expansion-panel> </mat-expansion-panel>

View File

@ -1,23 +1,23 @@
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'; import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
import {AsyncPipe} from "@angular/common"; import {AsyncPipe} from "@angular/common";
import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component";
import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion"; import {MatAccordion, MatExpansionModule, MatExpansionPanel} from "@angular/material/expansion";
import {MatChipListboxChange, MatChipsModule} from "@angular/material/chips"; import {MatChipListboxChange, MatChipsModule} from "@angular/material/chips";
import {Observable, of} from "rxjs"; import {Observable, of} from "rxjs";
import {CampusBasicInfoResponse} from "@model/campusBasicInfoResponse"; import {CampusBasicInfoResponse} from "@model/campusBasicInfoResponse";
import {FormControl, ReactiveFormsModule} from "@angular/forms"; import {FormControl, ReactiveFormsModule} from "@angular/forms";
import {LectureHallResponse} from "@model/lectureHallResponse"; import {LectureHallResponse} from "@model/lectureHallResponse";
import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component";
@Component({ @Component({
selector: 'app-schedule-tabs-lecture-hall', selector: 'app-schedule-tabs-lecture-hall',
standalone: true, standalone: true,
imports: [ imports: [
MatChipsModule, MatChipsModule,
DataSpinnerComponent,
MatExpansionModule, MatExpansionModule,
AsyncPipe, AsyncPipe,
ReactiveFormsModule, ReactiveFormsModule,
MatAccordion MatAccordion,
LoadingIndicatorComponent
], ],
templateUrl: './schedule-tabs-lecture-hall.component.html', templateUrl: './schedule-tabs-lecture-hall.component.html',
styleUrl: './schedule-tabs-lecture-hall.component.css' styleUrl: './schedule-tabs-lecture-hall.component.css'
@ -29,12 +29,16 @@ export class ScheduleTabsLectureHallComponent {
@ViewChild('lecturePanel') lecturePanel!: MatExpansionPanel; @ViewChild('lecturePanel') lecturePanel!: MatExpansionPanel;
@Input() campuses: Observable<CampusBasicInfoResponse[]> = of([]); @Input() campuses: Observable<CampusBasicInfoResponse[]> = of([]);
@Input() campusesLoaded: boolean | null = false;
@Output() campusesLoadRetry: EventEmitter<void> = new EventEmitter<void>();
@Input() lectureHalls: Observable<LectureHallResponse[]> = of([]); @Input() lectureHalls: Observable<LectureHallResponse[]> = of([]);
@Input() lectureHallsLoaded: boolean | null = false;
@Output() lectureHallsLoadRetry: EventEmitter<number> = new EventEmitter<number>();
@Output() campusSelected = new EventEmitter<number>(); @Output() campusSelected = new EventEmitter<number>();
@Output() lectureHallSelected = new EventEmitter<number>(); @Output() lectureHallSelected = new EventEmitter<number>();
protected selectedCampus(event: MatChipListboxChange) { protected chooseCampus(event: MatChipListboxChange) {
this.chipLecture.reset(); this.chipLecture.reset();
if (event.value === undefined || event.value === null) { if (event.value === undefined || event.value === null) {
@ -49,7 +53,7 @@ export class ScheduleTabsLectureHallComponent {
this.campusSelected.emit(this.campusId!); this.campusSelected.emit(this.campusId!);
} }
protected selectedLectureHall(event: MatChipListboxChange) { protected chooseLectureHall(event: MatChipListboxChange) {
if (event.value === undefined || event.value === null) if (event.value === undefined || event.value === null)
return; return;

View File

@ -1,31 +1,34 @@
<!--suppress CssInvalidPropertyValue --> <!--suppress CssInvalidPropertyValue -->
<button mat-button [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" [id]="idButton">{{ buttonText }}</button> <button mat-button [matMenuTriggerFor]="menu" #menuTrigger="matMenuTrigger" [id]="idButton">{{ textButton }}</button>
<mat-menu #menu="matMenu" [hasBackdrop]="false" class="menu-options"> <mat-menu #menu="matMenu" [hasBackdrop]="false" class="menu-options">
<div (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" style="padding: 0 15px 15px"> <div (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()" style="padding: 0 15px 15px">
<div class="header-menu"> <div class="header-menu">
<mat-form-field appearance="outline" color="accent" style="display:flex;"> <mat-form-field appearance="outline" color="accent" style="display:flex;">
<input matInput placeholder="Поиск..." [(ngModel)]="searchQuery" [disabled]="data.length === 0"> <input matInput placeholder="Поиск..." [(ngModel)]="searchQuery" [disabled]="data.length === 0">
<button mat-icon-button matSuffix (click)="clearSearch()" [disabled]="data.length === 0"> <button mat-icon-button matSuffix (click)="clearSearchQuery()" [disabled]="data.length === 0">
<mat-icon style="color: var(--mdc-filled-button-label-text-color);">close</mat-icon> <mat-icon style="color: var(--mdc-filled-button-label-text-color);">close</mat-icon>
</button> </button>
</mat-form-field> </mat-form-field>
<div class="button-group"> <div class="button-group">
<mat-checkbox (click)="selectData()" [disabled]="data.length === 0" #chooseCheckbox/> <mat-checkbox (click)="checkData()" [disabled]="data.length === 0" #chooseCheckbox/>
<button mat-button (click)="clearAll()" [disabled]="data.length === 0">Очистить</button> <button mat-button (click)="clearAll()" [disabled]="data.length === 0">Очистить</button>
</div> </div>
<hr/> <hr/>
</div> </div>
@if (data.length === 0) { @if (data.length === 0) {
<app-data-spinner style="display: flex; justify-content: center;"/> <app-loading-indicator style="display: flex; justify-content: center;" [loading]="dataLoaded !== null"
(retryFunction)="retryLoadData.emit()"/>
} @else { } @else {
<mat-selection-list> <mat-selection-list>
@if (filteredData.value.length !== 0) { @if (filteredData.value.length !== 0) {
<cdk-virtual-scroll-viewport autosize [minBufferPx]="300" [maxBufferPx]="500" style="height: 250px; width: 260px; overflow-x: clip;"> <cdk-virtual-scroll-viewport autosize [minBufferPx]="300" [maxBufferPx]="500"
style="height: 250px; width: 260px; overflow-x: clip;">
<div *cdkVirtualFor="let item of filteredData"> <div *cdkVirtualFor="let item of filteredData">
<mat-list-option togglePosition="before" style="height: auto;" [value]="item.id" [selected]="item.selected" <mat-list-option togglePosition="before" style="height: auto;" [value]="item.id"
(click)="selectChange(item.id)"> [selected]="item.selected"
(click)="checkboxStateChange(item.id)">
<div style="text-wrap: balance;">{{ item.name }}</div> <div style="text-wrap: balance;">{{ item.name }}</div>
</mat-list-option> </mat-list-option>
<mat-divider style="margin: 10px 0;"/> <mat-divider style="margin: 10px 0;"/>

View File

@ -1,4 +1,4 @@
import {Component, HostListener, Input, ViewChild} from '@angular/core'; import {Component, EventEmitter, HostListener, Input, Output, ViewChild} from '@angular/core';
import {MatMenu, MatMenuTrigger} from "@angular/material/menu"; import {MatMenu, MatMenuTrigger} from "@angular/material/menu";
import {MatCheckbox} from "@angular/material/checkbox"; import {MatCheckbox} from "@angular/material/checkbox";
import {BehaviorSubject} from "rxjs"; import {BehaviorSubject} from "rxjs";
@ -6,12 +6,12 @@ import {MatButton, MatIconButton} from "@angular/material/button";
import {MatFormField, MatSuffix} from "@angular/material/form-field"; import {MatFormField, MatSuffix} from "@angular/material/form-field";
import {MatIcon} from "@angular/material/icon"; import {MatIcon} from "@angular/material/icon";
import {FormsModule} from "@angular/forms"; import {FormsModule} from "@angular/forms";
import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component";
import {MatListOption, MatSelectionList} from "@angular/material/list"; import {MatListOption, MatSelectionList} from "@angular/material/list";
import {ScrollingModule} from "@angular/cdk/scrolling"; import {ScrollingModule} from "@angular/cdk/scrolling";
import {ScrollingModule as ExperimentalScrollingModule} from '@angular/cdk-experimental/scrolling'; import {ScrollingModule as ExperimentalScrollingModule} from '@angular/cdk-experimental/scrolling';
import {MatDivider} from "@angular/material/divider"; import {MatDivider} from "@angular/material/divider";
import {MatInput} from "@angular/material/input"; import {MatInput} from "@angular/material/input";
import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component";
export interface SelectData { export interface SelectData {
id: number, id: number,
@ -30,7 +30,6 @@ export interface SelectData {
MatIcon, MatIcon,
FormsModule, FormsModule,
MatCheckbox, MatCheckbox,
DataSpinnerComponent,
MatSelectionList, MatSelectionList,
MatListOption, MatListOption,
MatDivider, MatDivider,
@ -38,7 +37,8 @@ export interface SelectData {
MatInput, MatInput,
MatSuffix, MatSuffix,
ScrollingModule, ScrollingModule,
ExperimentalScrollingModule ExperimentalScrollingModule,
LoadingIndicatorComponent
], ],
templateUrl: './schedule-tabs-other.component.html', templateUrl: './schedule-tabs-other.component.html',
styleUrl: './schedule-tabs-other.component.css' styleUrl: './schedule-tabs-other.component.css'
@ -49,10 +49,13 @@ export class ScheduleTabsOtherComponent {
protected data: SelectData[] = []; protected data: SelectData[] = [];
@Input() idButton!: string; @Input() idButton!: string;
@Input() buttonText!: string; @Input() textButton!: string;
@ViewChild('menuTrigger') menuTrigger!: MatMenuTrigger; @ViewChild('menuTrigger') menuTrigger!: MatMenuTrigger;
@ViewChild('chooseCheckbox') chooseCheckbox!: MatCheckbox; @ViewChild('chooseCheckbox') chooseCheckbox!: MatCheckbox;
@Input() dataLoaded: boolean | null = false;
@Output() retryLoadData: EventEmitter<void> = new EventEmitter<void>();
get selectedIds(): number[] { get selectedIds(): number[] {
return this.data.filter(x => x.selected).map(x => x.id); return this.data.filter(x => x.selected).map(x => x.id);
} }
@ -60,7 +63,7 @@ export class ScheduleTabsOtherComponent {
set Data(data: SelectData[]) { set Data(data: SelectData[]) {
this.data = data; this.data = data;
this.data.forEach(x => x.selected = false); this.data.forEach(x => x.selected = false);
this.filteredData.next(this.data) this.filteredData.next(this.data);
} }
protected get searchQuery(): string { protected get searchQuery(): string {
@ -83,7 +86,7 @@ export class ScheduleTabsOtherComponent {
)); ));
} }
protected clearSearch(): void { protected clearSearchQuery(): void {
this.searchQuery = ''; this.searchQuery = '';
} }
@ -101,7 +104,7 @@ export class ScheduleTabsOtherComponent {
this.updateCheckBox(); this.updateCheckBox();
} }
protected selectData() { protected checkData() {
const check: boolean = this.filteredData.value.some(x => !x.selected) && !this.filteredData.value.every(x => x.selected); const check: boolean = this.filteredData.value.some(x => !x.selected) && !this.filteredData.value.every(x => x.selected);
const updatedData = this.filteredData.value.map(data => { const updatedData = this.filteredData.value.map(data => {
@ -113,7 +116,7 @@ export class ScheduleTabsOtherComponent {
this.updateCheckBox(); this.updateCheckBox();
} }
selectChange(item: number) { protected checkboxStateChange(item: number) {
const data = this.data.find(x => x.id === item)!; const data = this.data.find(x => x.id === item)!;
data.selected = !data.selected; data.selected = !data.selected;
const updatedData = this.filteredData.value; const updatedData = this.filteredData.value;

View File

@ -1,13 +1,13 @@
<div class="search-content"> <div class="search-content">
@if (teachers.length === 0) { @if (professors.length === 0) {
<app-data-spinner/> <app-loading-indicator [loading]="professorsLoaded !== null" (retryFunction)="professorsLoadRetry.emit()"/>
} @else { } @else {
<mat-form-field color="accent" style="width: 100%;"> <mat-form-field color="accent" style="width: 100%;">
<input type="text" placeholder="Поиск..." matInput [formControl]="teacherControl" [matAutocomplete]="auto"> <input type="text" placeholder="Поиск..." matInput [formControl]="professorControl" [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="onOptionSelected($event)" <mat-autocomplete #auto="matAutocomplete" (optionSelected)="onOptionSelected($event)"
[autoActiveFirstOption]="false" [hideSingleSelectionIndicator]="true"> [autoActiveFirstOption]="false" [hideSingleSelectionIndicator]="true">
@for (option of filteredTeachers | async; track option) { @for (option of filteredProfessors | async; track option) {
<mat-option [value]="option.id"> <mat-option [value]="option.id">
{{ option.name }} {{ option.name }}
</mat-option> </mat-option>

View File

@ -3,9 +3,9 @@ import {MatFormField, MatInput} from "@angular/material/input";
import {FormControl, ReactiveFormsModule} from "@angular/forms"; import {FormControl, ReactiveFormsModule} from "@angular/forms";
import {MatAutocompleteModule, MatAutocompleteSelectedEvent} from "@angular/material/autocomplete"; import {MatAutocompleteModule, MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
import {AsyncPipe} from "@angular/common"; import {AsyncPipe} from "@angular/common";
import {DataSpinnerComponent} from "@component/data-spinner/data-spinner.component";
import {map, Observable, startWith} from "rxjs"; import {map, Observable, startWith} from "rxjs";
import {ProfessorResponse} from "@model/professorResponse"; import {ProfessorResponse} from "@model/professorResponse";
import {LoadingIndicatorComponent} from "@component/loading-indicator/loading-indicator.component";
@Component({ @Component({
selector: 'app-schedule-tabs-professor', selector: 'app-schedule-tabs-professor',
@ -13,43 +13,46 @@ import {ProfessorResponse} from "@model/professorResponse";
imports: [ imports: [
MatAutocompleteModule, MatAutocompleteModule,
MatFormField, MatFormField,
DataSpinnerComponent,
AsyncPipe, AsyncPipe,
ReactiveFormsModule, ReactiveFormsModule,
MatInput MatInput,
LoadingIndicatorComponent
], ],
templateUrl: './schedule-tabs-professor.component.html', templateUrl: './schedule-tabs-professor.component.html',
styleUrl: './schedule-tabs-professor.component.css' styleUrl: './schedule-tabs-professor.component.css'
}) })
export class ScheduleTabsProfessorComponent implements OnInit { export class ScheduleTabsProfessorComponent implements OnInit {
protected teacherControl = new FormControl(); protected professorControl = new FormControl();
protected filteredTeachers!: Observable<ProfessorResponse[]>; protected filteredProfessors!: Observable<ProfessorResponse[]>;
@Input() teachers: ProfessorResponse[] = []; @Input() professors: ProfessorResponse[] = [];
@Output() professorSelected = new EventEmitter<number>(); @Output() professorSelected = new EventEmitter<number>();
@Input() professorsLoaded: boolean | null = false;
@Output() professorsLoadRetry: EventEmitter<void> = new EventEmitter<void>();
ngOnInit(): void { ngOnInit(): void {
this.filteredTeachers = this.teacherControl.valueChanges.pipe( this.filteredProfessors = this.professorControl.valueChanges.pipe(
startWith(''), startWith(''),
map(value => this._filterTeachers(value)) map(value => this._filterProfessors(value))
); );
} }
private _filterTeachers(value: string | number): ProfessorResponse[] { private _filterProfessors(value: string | number): ProfessorResponse[] {
if (typeof value === 'string') { if (typeof value === 'string') {
if (value === '') return []; if (value === '') return [];
const filterValue = value.toLowerCase(); const filterValue = value.toLowerCase();
return this.teachers.filter(teacher => teacher.name.toLowerCase().includes(filterValue)); return this.professors.filter(teacher => teacher.name.toLowerCase().includes(filterValue));
} else { } else {
const selectedTeacher = this.teachers.find(teacher => teacher.id === value); const selectedTeacher = this.professors.find(teacher => teacher.id === value);
return selectedTeacher ? [selectedTeacher] : []; return selectedTeacher ? [selectedTeacher] : [];
} }
} }
protected onOptionSelected(event: MatAutocompleteSelectedEvent) { protected onOptionSelected(event: MatAutocompleteSelectedEvent) {
const selectedOption = this.teachers.find(teacher => teacher.id === event.option.value); const selectedOption = this.professors.find(teacher => teacher.id === event.option.value);
if (selectedOption) { if (selectedOption) {
this.teacherControl.setValue(selectedOption.name); this.professorControl.setValue(selectedOption.name);
this.professorSelected.emit(selectedOption.id); this.professorSelected.emit(selectedOption.id);
} }
} }