src/app/features/location/location.datatype.ts
Properties |
|
Methods |
| getExportColumns | ||||||
getExportColumns(schemaField: EntitySchemaField)
|
||||||
|
Inherited from
DefaultDatatype
|
||||||
|
Export the human-readable location string (default column) plus separate columns for the individual address parts (street, house number, postcode, city) so they can be used flexibly for mail merge and similar processes (see #3715). The address parts are read from the flat top-level fields, which
transformToObjectFormat / importMapFunction populate via
Parameters :
Returns :
ExportColumnMapping[]
|
| Async importMapFunction | ||||||||||||
importMapFunction(val: any, schemaField?: any, additional?: LocationImportConfig)
|
||||||||||||
|
Inherited from
DefaultDatatype
|
||||||||||||
|
Parameters :
Returns :
Promise<GeoLocation>
|
| transformToObjectFormat | ||||||
transformToObjectFormat(value: GeoLocation)
|
||||||
|
Inherited from
DefaultDatatype
|
||||||
|
Parameters :
Returns :
GeoLocation
|
| Async anonymize | ||||||||||||||||
anonymize(value: EntityType, schemaField: EntitySchemaField, parent: any)
|
||||||||||||||||
|
Inherited from
DefaultDatatype
|
||||||||||||||||
|
(Partially) anonymize to "retain-anonymized" for reporting purposes without personal identifiable information.
Parameters :
Returns :
Promise<any>
|
| Static detectAllFieldsInEntity | ||||||||||||
detectAllFieldsInEntity(entityOrType: Entity | EntityConstructor, dataTypes: string | string[])
|
||||||||||||
|
Inherited from
DefaultDatatype
|
||||||||||||
|
Detect all fields of the given datatype(s) in an entity's schema.
Parameters :
Returns :
literal type[]
Array of matching fields with their id and schema definition. |
| Static detectFieldInEntity | ||||||||||||
detectFieldInEntity(entityOrType: Entity | EntityConstructor, dataTypes: string | string[])
|
||||||||||||
|
Inherited from
DefaultDatatype
|
||||||||||||
|
Detect the first field of the given datatype(s) in an entity's schema. Scans the schema for a field whose Subclasses typically override this without the extra
Parameters :
Returns :
string | undefined
The field name of the first matching field, or |
| getReferenceSelector | ||||||||||||
getReferenceSelector(fieldId: string, schemaField: EntitySchemaField, referencedId: string)
|
||||||||||||
|
Inherited from
DefaultDatatype
|
||||||||||||
|
Mango conditions matching documents whose The conditions are matched against the database format, so only the datatype that
writes that format can build them - which is why this lives here rather than in the
calling service. Return every shape the value may have been stored in rather than
branching on The caller treats these as candidates only and still post-filters in memory, so matching too much is harmless - matching too little silently loses references. Example :
Parameters :
Returns :
[] | undefined
the conditions to OR together, or |
| Async importMatchField | ||||||||||||||||
importMatchField(schemaField: EntitySchemaField, columns: ColumnImportInput[], importProcessingContext?: ImportProcessingContext)
|
||||||||||||||||
|
Inherited from
DefaultDatatype
|
||||||||||||||||
|
Map all columns mapped to one target field to the value(s) stored there. ImportService calls this once per target field per row with every column mapped to that field. The default handles trimming and array splitting and delegates each individual value to importMapFunction. Datatypes that need to consider several columns together (e.g. matching an entity by more than one property) override this. (isArray=true), or undefined if nothing was mapped
Parameters :
Returns :
Promise<unknown>
the value to store: a single value (isArray=false) or array (isArray=true), or undefined if nothing was mapped |
| normalizeSchemaField | ||||||||
normalizeSchemaField(schemaField: EntitySchemaField)
|
||||||||
|
Inherited from
DefaultDatatype
|
||||||||
|
Return the (potentially adjusted) schema field for this datatype. Called when schema fields are set up (e.g. from config), allowing the datatype to normalize or fill in required settings. Override this in a subclass to enforce constraints
(e.g. always setting
Parameters :
Returns :
EntitySchemaField
The schema field to use (default: unchanged) |
| sortValue | ||||||
sortValue(_fieldValue: EntityType)
|
||||||
|
Inherited from
DefaultDatatype
|
||||||
|
Return a comparable primitive for sorting this field's value in a list column.
Return
Parameters :
Returns :
number | string | undefined
|
| transformToDatabaseFormat | ||||||||||||||||
transformToDatabaseFormat(value: EntityType, schemaField?: EntitySchemaField, parent?: Entity)
|
||||||||||||||||
|
Inherited from
DefaultDatatype
|
||||||||||||||||
|
Transformation function taking a value in the format that is used in entity instances and returning the value in the format used in database objects. Example :
Parameters :
Returns :
DBType
|
| Static dataType |
Type : string
|
Default value : "location"
|
|
Inherited from
DefaultDatatype
|
| editComponent |
Type : string
|
Default value : "EditLocation"
|
|
Inherited from
DefaultDatatype
|
| importConfigComponent |
Type : string
|
Default value : "LocationImportConfig"
|
|
Inherited from
DefaultDatatype
|
| Static label |
Type : string
|
Default value : $localize`:datatype-label:location (address + map)`
|
|
Inherited from
DefaultDatatype
|
| viewComponent |
Type : string
|
Default value : "ViewLocation"
|
|
Inherited from
DefaultDatatype
|
| Readonly importAllowsMultiMapping |
Type : boolean
|
Default value : false
|
|
Inherited from
DefaultDatatype
|
|
Whether this datatype allows multiple values to be mapped to the same entity field during import. |
| Optional importConfigDialog |
Type : string
|
|
Inherited from
DefaultDatatype
|
|
A dialog component in which the import transformation is configured (see MappingDialogData for the data it receives). Datatypes that require such a dialog get it opened automatically as soon as the user maps a column to one of their fields, so that the suggested settings are reviewed and confirmed rather than silently skipped. |
| Readonly internalValidators |
Type : ValidatorFn[]
|
Default value : []
|
|
Inherited from
DefaultDatatype
|
|
Validators that every value of this datatype has to pass in entity forms,
in addition to the |
import { Injectable, inject } from "@angular/core";
import { lastValueFrom } from "rxjs";
import {
DefaultDatatype,
ExportColumnMapping,
} from "../../core/entity/default-datatype/default.datatype";
import { GeoLocation } from "./geo-location";
import { OpenStreetMapsSearchResult, GeoService } from "./geo.service";
import { LocationImportConfig } from "./location-import-config/location-import-config.component";
import { EntitySchemaField } from "../../core/entity/schema/entity-schema-field";
@Injectable()
export class LocationDatatype extends DefaultDatatype<
GeoLocation,
GeoLocation
> {
private geoService = inject(GeoService);
static override dataType = "location";
static override label: string = $localize`:datatype-label:location (address + map)`;
override editComponent = "EditLocation";
override viewComponent = "ViewLocation";
override importConfigComponent = "LocationImportConfig";
/**
* Export the human-readable location string (default column) plus separate
* columns for the individual address parts (street, house number, postcode, city)
* so they can be used flexibly for mail merge and similar processes (see #3715).
*
* The address parts are read from the flat top-level fields, which
* {@link transformToObjectFormat} / {@link importMapFunction} populate via
* `enrichGeoLocation` (copying `geoLookup.address` up, folding village/town
* into city and stringifying postcode). Every value reaching export is
* therefore already enriched, so no `geoLookup.address` fallback is needed.
*/
override getExportColumns(
schemaField: EntitySchemaField,
): ExportColumnMapping<GeoLocation>[] {
if (!schemaField.label) {
return [];
}
const label = schemaField.label;
const part = (
keySuffix: string,
partLabel: string,
pick: (value: GeoLocation) => string | undefined,
): ExportColumnMapping<GeoLocation> => ({
keySuffix,
label: `${label} (${partLabel})`,
resolveValue: (value) => (value ? pick(value) : undefined),
});
return [
{
keySuffix: "",
label,
// return the whole object so the CSV/XLSX transformation extracts the locationString
resolveValue: (value) => value,
},
part(
"_street",
$localize`:Location export column:street`,
(value) => value.road,
),
part(
"_house_number",
$localize`:Location export column:house number`,
(value) => value.house_number,
),
part(
"_postcode",
$localize`:Location export column:postcode`,
(value) => value.postcode,
),
part(
"_city",
$localize`:Location export column:city`,
(value) => value.city,
),
];
}
override transformToObjectFormat(value: GeoLocation): GeoLocation {
if (typeof value !== "object") {
// until we have an extended location datatype that includes a custom address addition field, discard invalid values (e.g. in case datatype was changed)
return undefined;
}
// migrate from legacy format
if (
!value.hasOwnProperty("locationString") &&
!value.hasOwnProperty("geoLookup")
) {
value = {
geoLookup: value as unknown as OpenStreetMapsSearchResult,
};
}
// fix errors from broken migrations
while (value?.geoLookup && "geoLookup" in value.geoLookup) {
value.geoLookup = (
value.geoLookup as { geoLookup: OpenStreetMapsSearchResult }
).geoLookup;
}
if (!value.locationString) {
value.locationString = value.geoLookup?.display_name ?? "";
}
return this.geoService.enrichGeoLocation(value);
}
override async importMapFunction(
val: any,
schemaField?: any,
additional?: LocationImportConfig,
): Promise<GeoLocation> {
if (!val) {
return undefined;
}
let geoResults: OpenStreetMapsSearchResult[] | undefined;
if (!additional?.skipAddressLookup) {
try {
geoResults = await lastValueFrom(this.geoService.lookup(val));
} catch {
geoResults = undefined;
}
}
return this.geoService.enrichGeoLocation({
locationString: val,
geoLookup: geoResults ? geoResults[0] : undefined,
});
}
}