src/app/core/entity/schema/entity-schema.service.ts
Transform between entity instances and database objects based on the dataType set for properties in Entity classes using the DatabaseField annotation.
You can inject the EntitySchemaService in your code to register your custom DefaultDatatype implementations.
This service is used by the EntityMapperService to internally transform objects. You should normally use the EntityMapperService instead of transforming objects yourself with the EntitySchemaService.
also see the How-To Guides:
Methods |
constructor(injector: Injector)
|
||||||
Parameters :
|
getComponent | |||||||||||||||
getComponent(propertySchema: EntitySchemaField, mode: "view" | "edit")
|
|||||||||||||||
Get the name of the component that should display this property.
The edit component has to be a registered component. Components that are registered contain the
Parameters :
Returns :
string
string The name of the component which should display this property |
Public getDatatypeOrDefault | |||||||||||||||
getDatatypeOrDefault(datatypeName: string, failSilently)
|
|||||||||||||||
Get the datatype for the giving name (or the default datatype if no other registered type fits)
Parameters :
Returns :
any
|
Public loadDataIntoEntity | ||||||||||||
loadDataIntoEntity(entity: E, data: any)
|
||||||||||||
Type parameters :
|
||||||||||||
Helper function to assign the giving data to the given entity instance after transforming it according to the schema.
Parameters :
Returns :
E
|
Public transformDatabaseToEntityFormat | ||||||||||||
transformDatabaseToEntityFormat(data: any, schema: EntitySchema)
|
||||||||||||
Type parameters :
|
||||||||||||
Transform a database object to entity format according to the schema.
Parameters :
Returns :
T
|
Public transformEntityToDatabaseFormat | ||||||||||||
transformEntityToDatabaseFormat(entity: Entity, schema?: EntitySchema)
|
||||||||||||
Transform an entity instance to a database object according to the schema.
Parameters :
Returns :
any
|
valueToDatabaseFormat | ||||||||||||
valueToDatabaseFormat(value: any, schemaField: EntitySchemaField, entity?: Entity)
|
||||||||||||
Transform a single value into database format
Parameters :
Returns :
any
|
valueToEntityFormat | ||||||||||||
valueToEntityFormat(value: any, schemaField: EntitySchemaField, dataObject?: any)
|
||||||||||||
Transform a single value into entity format
Parameters :
Returns :
any
|
import { Entity } from "../model/entity";
import { Injectable, Injector } from "@angular/core";
import { EntitySchema } from "./entity-schema";
import { EntitySchemaField } from "./entity-schema-field";
import { DefaultDatatype } from "../default-datatype/default.datatype";
import { asArray } from "app/utils/asArray";
/**
* Transform between entity instances and database objects
* based on the dataType set for properties in Entity classes using the {@link DatabaseField} annotation.
*
* You can inject the EntitySchemaService in your code to register your custom {@link DefaultDatatype} implementations.
*
* This service is used by the {@link EntityMapperService} to internally transform objects.
* You should normally use the EntityMapperService instead of transforming objects yourself with the EntitySchemaService.
*
* also see the How-To Guides:
* - [Create A New Entity Type]{@link /additional-documentation/how-to-guides/create-a-new-entity-type.html}
*/
@Injectable({ providedIn: "root" })
export class EntitySchemaService {
/**
* Internal cache of datatype implementations.
*/
private schemaTypes = new Map<string, DefaultDatatype>();
private defaultDatatype: DefaultDatatype = new DefaultDatatype();
constructor(private injector: Injector) {}
/**
* Get the datatype for the giving name (or the default datatype if no other registered type fits)
* @param datatypeName The key/name of the datatype
* @param failSilently If set to 'true' no error is thrown if datatype does not exist
*/
public getDatatypeOrDefault(datatypeName: string, failSilently = false) {
if (!datatypeName) {
return this.defaultDatatype;
}
if (this.schemaTypes.has(datatypeName)) {
return this.schemaTypes.get(datatypeName);
}
// use Injector instead of normal dependency injection in the constructor, because some Datatypes use the SchemaService (--> Circular Dependency)
const dataTypes: DefaultDatatype[] = this.injector.get(
DefaultDatatype,
) as unknown as DefaultDatatype[];
let dataType = dataTypes.find((d) => d.dataType === datatypeName);
if (dataType) {
this.schemaTypes.set(datatypeName, dataType);
return dataType;
} else if (!failSilently) {
throw new Error(`Data type "${datatypeName}" does not exist`);
}
}
/**
* Transform a database object to entity format according to the schema.
* @param data The database object that will be transformed to the given entity format
* @param schema A schema defining the transformation
*/
public transformDatabaseToEntityFormat<T = Entity>(
data: any,
schema: EntitySchema,
): T {
const transformed = {};
for (const key of schema.keys()) {
const schemaField: EntitySchemaField = schema.get(key);
if (data[key] === undefined) {
continue;
}
const newValue = this.valueToEntityFormat(data[key], schemaField, data);
if (newValue !== undefined) {
transformed[key] = newValue;
}
if (schemaField.generateIndex) {
throw new Error('schema option "isIndexed" not implemented yet');
}
}
return transformed as T;
}
/**
* Helper function to assign the giving data to the given entity instance after transforming it according to the schema.
* @param entity An entity instance whose properties will be overwritten with the transformed data
* @param data The database object that will be transformed and assigned to the entity
*/
public loadDataIntoEntity<E extends Entity>(entity: E, data: any): E {
const transformed = this.transformDatabaseToEntityFormat(
data,
(<typeof Entity>entity.constructor).schema,
);
return Object.assign(entity, transformed);
}
/**
* Transform an entity instance to a database object according to the schema.
* @param entity The object (an instance of an entity type)
* @param schema The schema of the entity (if not explicitly defined the schema of the given entity is used)
*/
public transformEntityToDatabaseFormat(
entity: Entity,
schema?: EntitySchema,
): any {
if (!schema) {
schema = entity.getSchema();
}
const data = {};
for (const key of schema.keys()) {
let value = entity[key];
const schemaField: EntitySchemaField = schema.get(key);
if (value === undefined) {
// skip and keep undefined
continue;
}
try {
data[key] = this.valueToDatabaseFormat(value, schemaField, entity);
} catch (err) {
throw new Error(`Transformation for ${key} failed: ${err}`);
}
if (data[key] === undefined) {
delete data[key];
}
}
return data;
}
/**
* Get the name of the component that should display this property.
* The edit component has to be a registered component. Components that are registered contain the `DynamicComponent`
* decorator
*
* @param propertySchema The schema definition of the attribute for which a component should be get
* @param mode (Optional) The mode for which a component is required. Default is "view".
* @returns string The name of the component which should display this property
*/
getComponent(
propertySchema: EntitySchemaField,
mode: "view" | "edit" = "view",
): string {
if (!propertySchema) {
return undefined;
}
const componentAttribute =
mode === "view" ? "viewComponent" : "editComponent";
if (propertySchema[componentAttribute]) {
return propertySchema[componentAttribute];
}
const dataType = this.getDatatypeOrDefault(propertySchema.dataType);
if (dataType?.[componentAttribute]) {
return dataType[componentAttribute];
}
}
/**
* Transform a single value into database format
* @param value
* @param schemaField
* @param entity
*/
valueToDatabaseFormat(
value: any,
schemaField: EntitySchemaField,
entity?: Entity,
) {
if (value === null) {
// keep 'null' to be able to explicitly mark a value as being reset
return null;
}
const dataType = this.getDatatypeOrDefault(schemaField.dataType);
if (schemaField.isArray) {
return asArray(value).map((v) =>
dataType.transformToDatabaseFormat(v, schemaField, entity),
);
} else {
return dataType.transformToDatabaseFormat(value, schemaField, entity);
}
}
/**
* Transform a single value into entity format
* @param value
* @param schemaField
* @param dataObject
*/
valueToEntityFormat(
value: any,
schemaField: EntitySchemaField,
dataObject?: any,
) {
if (value === null) {
// keep 'null' to be able to explicitly mark a value as being reset
return null;
}
const dataType = this.getDatatypeOrDefault(schemaField.dataType);
if (schemaField.isArray) {
return asArray(value).map((v) =>
dataType.transformToObjectFormat(v, schemaField, dataObject),
);
} else {
return dataType.transformToObjectFormat(value, schemaField, dataObject);
}
}
}