File
Description
This component can be used to display an entity in more detail.
As an abstract base component, this provides functionality to load an entity
and leaves the UI and special functionality to components that extend this class, like EntityDetailsComponent.
Implements
Index
Properties
|
|
|
Methods
|
|
|
Inputs
|
|
|
Methods
|
Protected
Async
loadEntity
|
loadEntity()
|
|
|
|
|
|
Protected
onEntityUpdated
|
onEntityUpdated()
|
|
|
Hook called whenever the entity is updated via the live subscription (e.g. after save or anonymize).
Subclasses can override this to react to entity changes beyond what markForCheck() provides.
|
|
Protected
subscribeToEntityChanges
|
subscribeToEntityChanges()
|
|
|
|
|
|
Protected
Readonly
ability
|
Type : unknown
|
Default value : inject(EntityAbility)
|
|
|
|
Protected
Readonly
cdr
|
Type : unknown
|
Default value : inject(ChangeDetectorRef)
|
|
|
|
Protected
Readonly
entities
|
Type : unknown
|
Default value : inject(EntityRegistry)
|
|
|
|
Protected
Readonly
entityMapperService
|
Type : unknown
|
Default value : inject(EntityMapperService)
|
|
|
|
Protected
Readonly
router
|
Type : unknown
|
Default value : inject(Router)
|
|
|
|
Protected
Readonly
unsavedChanges
|
Type : unknown
|
Default value : inject(UnsavedChangesService)
|
|
|
import {
ChangeDetectorRef,
Directive,
inject,
Input,
OnChanges,
SimpleChanges,
} from "@angular/core";
import { Router } from "@angular/router";
import { Entity, EntityConstructor } from "../../entity/model/entity";
import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service";
import { EntityAbility } from "../../permissions/ability/entity-ability";
import { EntityRegistry } from "../../entity/database-entity.decorator";
import { filter } from "rxjs/operators";
import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
import { Subscription } from "rxjs";
import { UnsavedChangesService } from "../form/unsaved-changes.service";
import { Logging } from "../../logging/logging.service";
/**
* This component can be used to display an entity in more detail.
* As an abstract base component, this provides functionality to load an entity
* and leaves the UI and special functionality to components that extend this class, like EntityDetailsComponent.
*/
@UntilDestroy()
@Directive()
export abstract class AbstractEntityDetailsComponent implements OnChanges {
protected readonly entityMapperService = inject(EntityMapperService);
protected readonly entities = inject(EntityRegistry);
protected readonly ability = inject(EntityAbility);
protected readonly router = inject(Router);
protected readonly unsavedChanges = inject(UnsavedChangesService);
protected readonly cdr = inject(ChangeDetectorRef);
isLoading: boolean;
private changesSubscription: Subscription;
@Input() entityType: string;
entityConstructor: EntityConstructor;
@Input() id: string;
@Input() entity: Entity;
async ngOnChanges(changes: SimpleChanges) {
if (changes.entityType) {
this.entityConstructor = this.entities.get(this.entityType);
}
if (changes.id) {
await this.loadEntity();
this.subscribeToEntityChanges();
}
}
/**
* Hook called whenever the entity is updated via the live subscription (e.g. after save or anonymize).
* Subclasses can override this to react to entity changes beyond what markForCheck() provides.
*/
protected onEntityUpdated() {}
protected subscribeToEntityChanges() {
const fullId = Entity.createPrefixedId(this.entityType, this.id);
this.changesSubscription?.unsubscribe();
this.changesSubscription = this.entityMapperService
.receiveUpdates(this.entityConstructor)
.pipe(
filter(({ entity }) => entity.getId() === fullId),
filter(({ type }) => type !== "remove"),
untilDestroyed(this),
)
.subscribe(({ entity }) => {
this.entity = entity;
this.onEntityUpdated();
this.cdr.markForCheck();
});
}
protected async loadEntity() {
this.isLoading = true;
if (this.id === "new") {
if (this.ability.cannot("create", this.entityConstructor)) {
this.router.navigate([""]);
return;
}
this.entity = new this.entityConstructor();
this.isLoading = false;
return;
}
try {
this.entity = await this.entityMapperService.load(
this.entityConstructor,
this.id,
);
} catch (error) {
if (error?.status !== 404) {
Logging.warn("Error loading record", error);
}
this.entity = null;
}
if (!this.entity) {
await this.router.navigate(["/404"]);
}
this.isLoading = false;
this.cdr.markForCheck();
}
}