Skip to content

Instantly share code, notes, and snippets.

@angelo510
Created October 14, 2019 16:43
Show Gist options
  • Save angelo510/c749c9addfcf271408dc2e11f987b7bd to your computer and use it in GitHub Desktop.
Save angelo510/c749c9addfcf271408dc2e11f987b7bd to your computer and use it in GitHub Desktop.
Example of Angular component for best rxjs practice
import { Component, forwardRef, Input } from "@angular/core";
import { NG_VALUE_ACCESSOR } from "@angular/forms";
import { DateTime } from "luxon";
import { BehaviorSubject, combineLatest, of } from "rxjs";
import { filter, map, shareReplay, startWith, switchMap } from "rxjs/operators";
import { AppointmentsService } from "src/appointments/appointments.service";
import { AppointmentSlot } from "src/appointments/fields/appointment-slot.model";
import { Day } from "src/core/day.model";
import { BaseFieldComponent } from "src/core/forms/base-field.component";
import { SitesService } from "src/core/sites";
import { ClosuresService } from "src/settings/closures";
import { Dock } from "src/settings/docks/dock.model";
import { DocksService } from "src/settings/docks/docks.service";
import { DoorGroup } from "src/settings/door-groups/door-group.model";
import { DoorGroupsService } from "src/settings/door-groups/door-groups.service";
import { DoorsService } from "src/settings/doors/doors.service";
import { ReservationsService } from "src/settings/reservations/reservations.service";
import { isExistent } from "src/utils";
@Component({
selector: "mr-schedule-field",
templateUrl: "./schedule-field.component.html",
styleUrls: ["./schedule-field.component.scss"],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => ScheduleFieldComponent),
},
],
})
export class ScheduleFieldComponent extends BaseFieldComponent<
AppointmentSlot
> {
public constructor(
private readonly sites: SitesService,
private readonly appointments: AppointmentsService,
private readonly closures: ClosuresService,
private readonly reservations: ReservationsService,
private readonly doors: DoorsService,
private readonly doorGroups: DoorGroupsService,
private readonly docks: DocksService,
) {
super();
}
@Input() public readonly isDaySelectable = false;
@Input() public set day(value: Day | null) {
this.selectedDaySubject.next(value);
}
private readonly selectedDaySubject = new BehaviorSubject<Day | null>(null);
public readonly selectedDayChanges = this.selectedDaySubject.pipe(
filter(isExistent),
);
private readonly selectedDateChanges = combineLatest([
this.selectedDayChanges,
this.sites.selectedChanges,
]).pipe(map(([day, site]) => day.toDateTime(Day.startOfDay, site.timeZone)));
private readonly isReceivingDayChanges = this.selectedDateChanges.pipe(
// If the selected date is the same day as the current moment in time
// (automatically compared against the date's time zone, which is the
// site's time zone) then it's receiving/game day.
map(date => date.hasSame(DateTime.utc(), "day")),
);
public readonly closuresChanges = combineLatest([
this.isReceivingDayChanges.pipe(
switchMap(isReceivingDay =>
this.closures.getListChanges({ isReceivingDay }),
),
filter(isExistent),
),
this.selectedDayChanges,
]).pipe(
map(([closures, day]) =>
closures.filter(closure => closure.includesDay(day)),
),
);
public readonly reservationsChanges = combineLatest([
this.reservations.changes.pipe(filter(isExistent)),
this.selectedDayChanges,
]).pipe(
map(([reservations, day]) =>
reservations.filter(
reservation => reservation.isActive && reservation.includesDay(day),
),
),
);
@Input() public set hideAppointments(value: boolean) {
this.hideAppointmentsSubject.next(value);
}
private readonly hideAppointmentsSubject = new BehaviorSubject(false);
@Input() public readonly appointmentDuration: number | null = null;
private readonly selectedDocksSubject = new BehaviorSubject<Dock[]>([]);
private readonly selectedDoorGroupsSubject = new BehaviorSubject<DoorGroup[]>(
[],
);
public readonly appointmentsChanges = combineLatest([
this.hideAppointmentsSubject,
this.selectedDateChanges.pipe(
map(date => date.until(date.plus({ days: 1 }))),
),
]).pipe(
switchMap(([hide, dateRange]) =>
hide
? of([])
: this.appointments.getListChanges({
dateRange,
areCancelledExcluded: true,
}),
),
shareReplay(1),
);
public readonly docksChanges = this.docks.changes;
public readonly doorGroupsChanges = this.doorGroups.changes;
public readonly doorsChanges = combineLatest([
this.doors.changes,
this.selectedDocksSubject,
this.selectedDoorGroupsSubject,
]).pipe(
map(
([doors, docks, doorGroups]) =>
doors &&
doors.filter(
door =>
(!docks.length || docks.includes(door.dock)) &&
(!doorGroups.length || doorGroups.includes(door.doorGroup)),
),
),
shareReplay(1),
);
public readonly isLoadedChanges = combineLatest([
this.appointmentsChanges,
this.doorsChanges,
]).pipe(
map(([appointments, doors]) => !!appointments && !!doors),
startWith(false),
);
public selectedHours = 16;
public setSelectedDay(day: Day): void {
this.selectedDaySubject.next(day);
}
public setSelectedHours(selectedHours: number): void {
this.selectedHours = selectedHours;
}
public setSelectedDocks(selectedDocks: Dock[]): void {
this.selectedDocksSubject.next(selectedDocks);
}
public setSelectedDoorGroups(selectedDoorGroups: DoorGroup[]): void {
this.selectedDoorGroupsSubject.next(selectedDoorGroups);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment