123 changed files with 1447 additions and 573 deletions
@ -1,36 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2025 The Thingsboard Authors |
|
||||
* |
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||
* you may not use this file except in compliance with the License. |
|
||||
* You may obtain a copy of the License at |
|
||||
* |
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||
* |
|
||||
* Unless required by applicable law or agreed to in writing, software |
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||
* See the License for the specific language governing permissions and |
|
||||
* limitations under the License. |
|
||||
*/ |
|
||||
package org.thingsboard.server.queue.edqs; |
|
||||
|
|
||||
import lombok.Getter; |
|
||||
|
|
||||
@Getter |
|
||||
public enum EdqsQueue { |
|
||||
|
|
||||
EVENTS("edqs.events", false, false), |
|
||||
STATE("edqs.state", true, true); |
|
||||
|
|
||||
private final String topic; |
|
||||
private final boolean readFromBeginning; |
|
||||
private final boolean stopWhenRead; |
|
||||
|
|
||||
EdqsQueue(String topic, boolean readFromBeginning, boolean stopWhenRead) { |
|
||||
this.topic = topic; |
|
||||
this.readFromBeginning = readFromBeginning; |
|
||||
this.stopWhenRead = stopWhenRead; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -0,0 +1,80 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2025 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<div [formGroup]="dataSourceFormGroup" class="tb-form-table-row tb-additional-map-data-source-row"> |
||||
|
<div class="tb-source-field"> |
||||
|
<mat-form-field class="tb-ds-type-field tb-inline-field" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<mat-select formControlName="dsType"> |
||||
|
<mat-option *ngFor="let type of datasourceTypes" [value]="type"> |
||||
|
{{ datasourceTypesTranslations.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field |
||||
|
*ngIf="dataSourceFormGroup.get('dsType').value === DatasourceType.function" |
||||
|
class="tb-label-field tb-inline-field" appearance="outline" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="dsLabel" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
</mat-form-field> |
||||
|
<tb-entity-autocomplete |
||||
|
class="tb-device-field" |
||||
|
*ngIf="dataSourceFormGroup.get('dsType').value === DatasourceType.device" |
||||
|
required |
||||
|
inlineField |
||||
|
placeholder="{{ 'device.select-device' | translate }}" |
||||
|
[entityType]="EntityType.DEVICE" |
||||
|
formControlName="dsDeviceId"> |
||||
|
</tb-entity-autocomplete> |
||||
|
<tb-entity-alias-select |
||||
|
class="tb-entity-alias-field" |
||||
|
*ngIf="dataSourceFormGroup.get('dsType').value === DatasourceType.entity" |
||||
|
inlineField |
||||
|
tbRequired |
||||
|
[aliasController]="context.aliasController" |
||||
|
formControlName="dsEntityAliasId" |
||||
|
[callbacks]="context.callbacks"> |
||||
|
</tb-entity-alias-select> |
||||
|
</div> |
||||
|
<tb-data-keys |
||||
|
class="tb-data-keys-field" |
||||
|
formControlName="dataKeys" |
||||
|
inlineField |
||||
|
hideDataKeyColor |
||||
|
hideDataKeyDecimals |
||||
|
hideDataKeyUnits |
||||
|
disableDrag |
||||
|
[required]="true" |
||||
|
[widgetType]="widgetType.latest" |
||||
|
[widget]="context.widget" |
||||
|
[datasourceType]="dataSourceFormGroup.get('dsType').value" |
||||
|
[entityAliasId]="dataSourceFormGroup.get('dsEntityAliasId').value" |
||||
|
[deviceId]="dataSourceFormGroup.get('dsDeviceId').value" |
||||
|
[aliasController]="context.aliasController" |
||||
|
[callbacks]="context.callbacks" |
||||
|
[generateKey]="generateAdditionalDataKey"> |
||||
|
</tb-data-keys> |
||||
|
<div class="tb-form-table-row-cell-buttons"> |
||||
|
<div class="tb-remove-button"> |
||||
|
<button type="button" |
||||
|
mat-icon-button |
||||
|
(click)="dataSourceRemoved.emit()" |
||||
|
matTooltip="{{ 'widgets.maps.data-layer.remove-datasource' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>delete</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,56 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2025 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
@import '../../../../../../../../../scss/constants'; |
||||
|
|
||||
|
.tb-form-table-row.tb-additional-map-data-source-row { |
||||
|
|
||||
|
.tb-source-field { |
||||
|
flex: 1 1 50%; |
||||
|
display: flex; |
||||
|
gap: 12px; |
||||
|
.tb-ds-type-field, .tb-label-field, .tb-device-field, .tb-entity-alias-field { |
||||
|
flex: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-data-keys-field { |
||||
|
flex: 1 1 50%; |
||||
|
min-width: 0; |
||||
|
} |
||||
|
|
||||
|
.tb-remove-button { |
||||
|
width: 40px; |
||||
|
min-width: 40px; |
||||
|
} |
||||
|
|
||||
|
@media #{$mat-lt-lg} { |
||||
|
.tb-source-field { |
||||
|
flex-direction: column; |
||||
|
flex: 1 1 30%; |
||||
|
} |
||||
|
.tb-data-keys-field { |
||||
|
flex: 1 1 70%; |
||||
|
} |
||||
|
@media #{$mat-lt-md} { |
||||
|
.tb-source-field { |
||||
|
flex: 1 1 50%; |
||||
|
} |
||||
|
.tb-data-keys-field { |
||||
|
flex: 1 1 50%; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,207 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2025 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import { |
||||
|
ChangeDetectorRef, |
||||
|
Component, |
||||
|
DestroyRef, |
||||
|
EventEmitter, |
||||
|
forwardRef, |
||||
|
Input, |
||||
|
OnInit, |
||||
|
Output, |
||||
|
ViewEncapsulation |
||||
|
} from '@angular/core'; |
||||
|
import { |
||||
|
ControlValueAccessor, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
UntypedFormBuilder, |
||||
|
UntypedFormGroup, |
||||
|
Validators |
||||
|
} from '@angular/forms'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
import { AdditionalMapDataSourceSettings } from '@shared/models/widget/maps/map.models'; |
||||
|
import { DataKey, DatasourceType, datasourceTypeTranslationMap, widgetType } from '@shared/models/widget.models'; |
||||
|
import { EntityType } from '@shared/models/entity-type.models'; |
||||
|
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
||||
|
import { genNextLabelForDataKeys } from '@core/utils'; |
||||
|
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-additional-map-data-source-row', |
||||
|
templateUrl: './additional-map-data-source-row.component.html', |
||||
|
styleUrls: ['./additional-map-data-source-row.component.scss'], |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => AdditionalMapDataSourceRowComponent), |
||||
|
multi: true |
||||
|
} |
||||
|
], |
||||
|
encapsulation: ViewEncapsulation.None |
||||
|
}) |
||||
|
export class AdditionalMapDataSourceRowComponent implements ControlValueAccessor, OnInit { |
||||
|
|
||||
|
DatasourceType = DatasourceType; |
||||
|
DataKeyType = DataKeyType; |
||||
|
|
||||
|
EntityType = EntityType; |
||||
|
|
||||
|
widgetType = widgetType; |
||||
|
|
||||
|
datasourceTypes: Array<DatasourceType> = []; |
||||
|
datasourceTypesTranslations = datasourceTypeTranslationMap; |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
context: MapSettingsContext; |
||||
|
|
||||
|
@Output() |
||||
|
dataSourceRemoved = new EventEmitter(); |
||||
|
|
||||
|
dataSourceFormGroup: UntypedFormGroup; |
||||
|
|
||||
|
generateAdditionalDataKey = this.generateDataKey.bind(this); |
||||
|
|
||||
|
modelValue: AdditionalMapDataSourceSettings; |
||||
|
|
||||
|
private propagateChange = (_val: any) => {}; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder, |
||||
|
private cd: ChangeDetectorRef, |
||||
|
private destroyRef: DestroyRef) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
if (this.context.functionsOnly) { |
||||
|
this.datasourceTypes = [DatasourceType.function]; |
||||
|
} else { |
||||
|
this.datasourceTypes = [DatasourceType.function, DatasourceType.device, DatasourceType.entity]; |
||||
|
} |
||||
|
this.dataSourceFormGroup = this.fb.group({ |
||||
|
dsType: [null, [Validators.required]], |
||||
|
dsLabel: [null, []], |
||||
|
dsDeviceId: [null, [Validators.required]], |
||||
|
dsEntityAliasId: [null, [Validators.required]], |
||||
|
dataKeys: [null, [Validators.required]] |
||||
|
}); |
||||
|
this.dataSourceFormGroup.valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe( |
||||
|
() => this.updateModel() |
||||
|
); |
||||
|
this.dataSourceFormGroup.get('dsType').valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe( |
||||
|
(newDsType: DatasourceType) => this.onDsTypeChanged(newDsType) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(_fn: any): void { |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (isDisabled) { |
||||
|
this.dataSourceFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.dataSourceFormGroup.enable({emitEvent: false}); |
||||
|
this.updateValidators(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
writeValue(value: AdditionalMapDataSourceSettings): void { |
||||
|
this.modelValue = value; |
||||
|
this.dataSourceFormGroup.patchValue( |
||||
|
{ |
||||
|
dsType: value?.dsType, |
||||
|
dsLabel: value?.dsLabel, |
||||
|
dsDeviceId: value?.dsDeviceId, |
||||
|
dsEntityAliasId: value?.dsEntityAliasId, |
||||
|
dataKeys: value?.dataKeys |
||||
|
}, {emitEvent: false} |
||||
|
); |
||||
|
this.updateValidators(); |
||||
|
this.cd.markForCheck(); |
||||
|
} |
||||
|
|
||||
|
private generateDataKey(key: DataKey): DataKey { |
||||
|
const dataKey = this.context.callbacks.generateDataKey(key.name, key.type, null, false, null); |
||||
|
const dataKeys: DataKey[] = this.dataSourceFormGroup.get('dataKeys').value || []; |
||||
|
dataKey.label = genNextLabelForDataKeys(dataKey.label, dataKeys); |
||||
|
return dataKey; |
||||
|
} |
||||
|
|
||||
|
private onDsTypeChanged(newDsType: DatasourceType) { |
||||
|
let updateModel = false; |
||||
|
const dataKeys: DataKey[] = this.dataSourceFormGroup.get('dataKeys').value; |
||||
|
if (dataKeys?.length) { |
||||
|
for (const key of dataKeys) { |
||||
|
updateModel = this.updateDataKeyToNewDsType(key, newDsType) || updateModel; |
||||
|
} |
||||
|
if (updateModel) { |
||||
|
this.dataSourceFormGroup.get('dataKeys').patchValue(dataKeys, {emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
this.updateValidators(); |
||||
|
if (updateModel) { |
||||
|
this.updateModel(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private updateDataKeyToNewDsType(dataKey: DataKey, newDsType: DatasourceType): boolean { |
||||
|
if (newDsType === DatasourceType.function) { |
||||
|
if (dataKey.type !== DataKeyType.function) { |
||||
|
dataKey.type = DataKeyType.function; |
||||
|
return true; |
||||
|
} |
||||
|
} else { |
||||
|
if (dataKey.type === DataKeyType.function) { |
||||
|
dataKey.type = DataKeyType.attribute; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private updateValidators() { |
||||
|
const dsType: DatasourceType = this.dataSourceFormGroup.get('dsType').value; |
||||
|
if (dsType === DatasourceType.function) { |
||||
|
this.dataSourceFormGroup.get('dsLabel').enable({emitEvent: false}); |
||||
|
this.dataSourceFormGroup.get('dsDeviceId').disable({emitEvent: false}); |
||||
|
this.dataSourceFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); |
||||
|
} else if (dsType === DatasourceType.device) { |
||||
|
this.dataSourceFormGroup.get('dsLabel').disable({emitEvent: false}); |
||||
|
this.dataSourceFormGroup.get('dsDeviceId').enable({emitEvent: false}); |
||||
|
this.dataSourceFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.dataSourceFormGroup.get('dsLabel').disable({emitEvent: false}); |
||||
|
this.dataSourceFormGroup.get('dsDeviceId').disable({emitEvent: false}); |
||||
|
this.dataSourceFormGroup.get('dsEntityAliasId').enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private updateModel() { |
||||
|
this.modelValue = {...this.modelValue, ...this.dataSourceFormGroup.value}; |
||||
|
this.propagateChange(this.modelValue); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2025 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<div class="tb-form-panel no-padding no-border"> |
||||
|
<div class="tb-form-table tb-additional-map-data-sources"> |
||||
|
<div class="tb-form-table-header no-padding-right"> |
||||
|
<div class="tb-form-table-header-cell tb-source-header" translate>widgets.maps.data-layer.source</div> |
||||
|
<div class="tb-form-table-header-cell tb-data-keys-header" translate>widgets.maps.data-layer.data-keys</div> |
||||
|
<div class="tb-form-table-header-cell tb-actions-header"></div> |
||||
|
</div> |
||||
|
<div *ngIf="dataSourcesFormArray().controls.length; else noDataSources" class="tb-form-table-body"> |
||||
|
<div *ngFor="let dataSourceControl of dataSourcesFormArray().controls; trackBy: trackByDataSource; let $index = index;"> |
||||
|
<tb-additional-map-data-source-row class="flex-1" |
||||
|
[context]="context" |
||||
|
[formControl]="dataSourceControl" |
||||
|
(dataSourceRemoved)="removeDataSource($index)"> |
||||
|
</tb-additional-map-data-source-row> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div> |
||||
|
<button type="button" mat-stroked-button color="primary" (click)="addDataSource()"> |
||||
|
{{ 'widgets.maps.data-layer.add-datasource' | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<ng-template #noDataSources> |
||||
|
<span class="tb-prompt flex items-center justify-center">{{ 'widgets.maps.data-layer.no-datasources' | translate }}</span> |
||||
|
</ng-template> |
||||
@ -0,0 +1,53 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2025 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
@import '../../../../../../../../../scss/constants'; |
||||
|
|
||||
|
.tb-additional-map-data-sources { |
||||
|
.tb-form-table-header-cell { |
||||
|
&.tb-source-header { |
||||
|
flex: 1 1 50%; |
||||
|
} |
||||
|
&.tb-data-keys-header { |
||||
|
flex: 1 1 50%; |
||||
|
} |
||||
|
&.tb-actions-header { |
||||
|
width: 40px; |
||||
|
min-width: 40px; |
||||
|
} |
||||
|
@media #{$mat-lt-lg} { |
||||
|
&.tb-source-header { |
||||
|
flex: 1 1 30%; |
||||
|
} |
||||
|
&.tb-data-keys-header { |
||||
|
flex: 1 1 70%; |
||||
|
} |
||||
|
@media #{$mat-lt-md} { |
||||
|
&.tb-source-header { |
||||
|
flex: 1 1 50%; |
||||
|
} |
||||
|
&.tb-data-keys-header { |
||||
|
flex: 1 1 50%; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tb-form-table-body { |
||||
|
tb-additional-map-data-source-row { |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,147 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2025 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; |
||||
|
import { |
||||
|
AbstractControl, |
||||
|
ControlValueAccessor, |
||||
|
NG_VALIDATORS, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
UntypedFormArray, |
||||
|
UntypedFormBuilder, |
||||
|
UntypedFormControl, |
||||
|
UntypedFormGroup, |
||||
|
Validator |
||||
|
} from '@angular/forms'; |
||||
|
import { mergeDeep } from '@core/utils'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
import { |
||||
|
AdditionalMapDataSourceSettings, |
||||
|
additionalMapDataSourceValid, |
||||
|
additionalMapDataSourceValidator, |
||||
|
defaultAdditionalMapDataSourceSettings |
||||
|
} from '@shared/models/widget/maps/map.models'; |
||||
|
import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-additional-map-data-sources', |
||||
|
templateUrl: './additional-map-data-sources.component.html', |
||||
|
styleUrls: ['./additional-map-data-sources.component.scss'], |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => AdditionalMapDataSourcesComponent), |
||||
|
multi: true |
||||
|
}, |
||||
|
{ |
||||
|
provide: NG_VALIDATORS, |
||||
|
useExisting: forwardRef(() => AdditionalMapDataSourcesComponent), |
||||
|
multi: true |
||||
|
} |
||||
|
], |
||||
|
encapsulation: ViewEncapsulation.None |
||||
|
}) |
||||
|
export class AdditionalMapDataSourcesComponent implements ControlValueAccessor, OnInit, Validator { |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
context: MapSettingsContext; |
||||
|
|
||||
|
dataSourcesFormGroup: UntypedFormGroup; |
||||
|
|
||||
|
private propagateChange = (_val: any) => {}; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder, |
||||
|
private destroyRef: DestroyRef) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.dataSourcesFormGroup = this.fb.group({ |
||||
|
dataSources: [this.fb.array([]), []] |
||||
|
}); |
||||
|
this.dataSourcesFormGroup.valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe( |
||||
|
() => { |
||||
|
let dataSources: AdditionalMapDataSourceSettings[] = this.dataSourcesFormGroup.get('dataSources').value; |
||||
|
if (dataSources) { |
||||
|
dataSources = dataSources.filter(dataSource => additionalMapDataSourceValid(dataSource)); |
||||
|
} |
||||
|
this.propagateChange(dataSources); |
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (isDisabled) { |
||||
|
this.dataSourcesFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.dataSourcesFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
writeValue(value: AdditionalMapDataSourceSettings[] | undefined): void { |
||||
|
const dataSources: AdditionalMapDataSourceSettings[] = value || []; |
||||
|
this.dataSourcesFormGroup.setControl('dataSources', this.prepareDataSourcesFormArray(dataSources), {emitEvent: false}); |
||||
|
} |
||||
|
|
||||
|
public validate(c: UntypedFormControl) { |
||||
|
const valid = this.dataSourcesFormGroup.valid; |
||||
|
return valid ? null : { |
||||
|
dataSources: { |
||||
|
valid: false, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
dataSourcesFormArray(): UntypedFormArray { |
||||
|
return this.dataSourcesFormGroup.get('dataSources') as UntypedFormArray; |
||||
|
} |
||||
|
|
||||
|
trackByDataSource(index: number, dataSourceControl: AbstractControl): any { |
||||
|
return dataSourceControl; |
||||
|
} |
||||
|
|
||||
|
removeDataSource(index: number) { |
||||
|
(this.dataSourcesFormGroup.get('dataSources') as UntypedFormArray).removeAt(index); |
||||
|
} |
||||
|
|
||||
|
addDataSource() { |
||||
|
const dataSource = mergeDeep<AdditionalMapDataSourceSettings>({} as AdditionalMapDataSourceSettings, |
||||
|
defaultAdditionalMapDataSourceSettings(this.context.functionsOnly)); |
||||
|
const dataSourcesArray = this.dataSourcesFormGroup.get('dataSources') as UntypedFormArray; |
||||
|
const dataSourceControl = this.fb.control(dataSource, [additionalMapDataSourceValidator]); |
||||
|
dataSourcesArray.push(dataSourceControl); |
||||
|
} |
||||
|
|
||||
|
private prepareDataSourcesFormArray(dataSources: AdditionalMapDataSourceSettings[]): UntypedFormArray { |
||||
|
const dataSourcesControls: Array<AbstractControl> = []; |
||||
|
dataSources.forEach((dataSource) => { |
||||
|
dataSourcesControls.push(this.fb.control(dataSource, [additionalMapDataSourceValidator])); |
||||
|
}); |
||||
|
return this.fb.array(dataSourcesControls); |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue