Browse Source

Merge branch 'rc'

pull/12340/head
Igor Kulikov 2 years ago
parent
commit
cef94b9708
  1. 4
      application/pom.xml
  2. 11
      application/src/main/data/upgrade/basic/schema_update.sql
  3. 2
      application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java
  4. 1
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  5. 2
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  6. 2
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  7. 16
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  8. 26
      application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java
  9. 4
      application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java
  10. 1
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java
  11. 3
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java
  12. 1
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java
  13. 1
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java
  14. 6
      application/src/main/resources/thingsboard.yml
  15. 3
      application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java
  16. 19
      application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java
  17. 8
      application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
  18. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java
  19. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java
  20. 13
      common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java
  21. 2
      dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java
  22. 4
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java
  23. 2
      dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java
  24. 10
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java
  25. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java
  26. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java
  27. 23
      pom.xml
  28. 9
      ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts
  29. 10
      ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts
  30. 5
      ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts
  31. 5
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.ts
  32. 9
      ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.ts
  33. 9
      ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts
  34. 9
      ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts
  35. 10
      ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts
  36. 14
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts
  37. 10
      ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.ts
  38. 5
      ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.ts
  39. 10
      ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts
  40. 10
      ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts
  41. 10
      ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts
  42. 5
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  43. 14
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.ts
  44. 14
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts
  45. 5
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts
  46. 10
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts
  47. 14
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts
  48. 10
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts
  49. 14
      ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts
  50. 14
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  51. 10
      ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.ts
  52. 10
      ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.ts
  53. 10
      ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts
  54. 5
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts
  55. 10
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts
  56. 4
      ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts
  57. 3
      ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.ts
  58. 10
      ui-ngx/src/app/modules/home/components/profile/tenant/tenant-profile-configuration.component.ts
  59. 28
      ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts
  60. 10
      ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts
  61. 10
      ui-ngx/src/app/modules/home/components/sms/aws-sns-provider-configuration.component.ts
  62. 10
      ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts
  63. 14
      ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.ts
  64. 10
      ui-ngx/src/app/modules/home/components/sms/twilio-sms-provider-configuration.component.ts
  65. 14
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts
  66. 10
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts
  67. 14
      ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts
  68. 9
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts
  69. 9
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts
  70. 14
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts
  71. 10
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-keys-table.component.ts
  72. 5
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts
  73. 9
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts
  74. 9
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts
  75. 10
      ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts
  76. 5
      ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts
  77. 30
      ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts
  78. 14
      ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts
  79. 10
      ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts
  80. 14
      ui-ngx/src/app/modules/home/components/widget/config/target-device.component.ts
  81. 18
      ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts
  82. 14
      ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts
  83. 13
      ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts
  84. 4
      ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts
  85. 10
      ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts
  86. 10
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts
  87. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts
  88. 26
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts
  89. 19
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts
  90. 38
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts
  91. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts
  92. 16
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts
  93. 11
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts
  94. 18
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts
  95. 11
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts
  96. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts
  97. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/auto-date-format-settings-panel.component.ts
  98. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss
  99. 27
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts
  100. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts

4
application/pom.xml

@ -390,6 +390,10 @@
</resource> </resource>
</resources> </resources>
<plugins> <plugins>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>

11
application/src/main/data/upgrade/basic/schema_update.sql

@ -193,6 +193,17 @@ $$
END; END;
$$; $$;
-- update constraint name
DO
$$
BEGIN
ALTER TABLE domain DROP CONSTRAINT IF EXISTS domain_unq_key;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'domain_name_key') THEN
ALTER TABLE domain ADD CONSTRAINT domain_name_key UNIQUE (name);
END IF;
END;
$$;
-- UPDATE RESOURCE JS_MODULE SUB TYPE START -- UPDATE RESOURCE JS_MODULE SUB TYPE START
UPDATE resource SET resource_sub_type = 'EXTENSION' WHERE resource_type = 'JS_MODULE' AND resource_sub_type IS NULL; UPDATE resource SET resource_sub_type = 'EXTENSION' WHERE resource_type = 'JS_MODULE' AND resource_sub_type IS NULL;

2
application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java

@ -88,7 +88,7 @@ public class AssetProfileController extends BaseController {
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId));
var result = checkAssetProfileId(assetProfileId, Operation.READ); var result = checkAssetProfileId(assetProfileId, Operation.READ);
if (inlineImages) { if (inlineImages) {
imageService.inlineImage(result); result = imageService.inlineImage(result);
} }
return result; return result;
} }

1
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -923,6 +923,7 @@ public abstract class BaseController {
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
info.put("userCredentialsEnabled", userCredentials.isEnabled()); info.put("userCredentialsEnabled", userCredentials.isEnabled());
info.put("userActivated", userCredentials.getActivateToken() == null);
info.put("lastLoginTs", userCredentials.getLastLoginTs()); info.put("lastLoginTs", userCredentials.getLastLoginTs());
} }

2
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -96,7 +96,7 @@ public class DeviceProfileController extends BaseController {
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
var result = checkDeviceProfileId(deviceProfileId, Operation.READ); var result = checkDeviceProfileId(deviceProfileId, Operation.READ);
if (inlineImages) { if (inlineImages) {
imageService.inlineImage(result); result = imageService.inlineImage(result);
} }
return result; return result;
} }

2
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -90,7 +90,7 @@ public class WidgetsBundleController extends BaseController {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
var result = checkWidgetsBundleId(widgetsBundleId, Operation.READ); var result = checkWidgetsBundleId(widgetsBundleId, Operation.READ);
if (inlineImages) { if (inlineImages) {
imageService.inlineImage(result); result = imageService.inlineImage(result);
} }
return result; return result;
} }

16
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -135,7 +135,7 @@ public abstract class EdgeGrpcSession implements Closeable {
private StreamObserver<RequestMsg> inputStream; private StreamObserver<RequestMsg> inputStream;
private StreamObserver<ResponseMsg> outputStream; private StreamObserver<ResponseMsg> outputStream;
private boolean connected; private volatile boolean connected;
private volatile boolean syncCompleted; private volatile boolean syncCompleted;
private EdgeVersion edgeVersion; private EdgeVersion edgeVersion;
@ -536,13 +536,15 @@ public abstract class EdgeGrpcSession implements Closeable {
public void processHighPriorityEvents() { public void processHighPriorityEvents() {
try { try {
List<EdgeEvent> highPriorityEvents = new ArrayList<>(); if (isConnected() && isSyncCompleted()) {
EdgeEvent event; List<EdgeEvent> highPriorityEvents = new ArrayList<>();
while ((event = highPriorityQueue.poll()) != null) { EdgeEvent event;
highPriorityEvents.add(event); while ((event = highPriorityQueue.poll()) != null) {
highPriorityEvents.add(event);
}
List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(highPriorityEvents);
sendDownlinkMsgsPack(downlinkMsgsPack).get();
} }
List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(highPriorityEvents);
sendDownlinkMsgsPack(downlinkMsgsPack).get();
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Failed to process high priority events", sessionId, e); log.error("[{}] Failed to process high priority events", sessionId, e);
} }

26
application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java

@ -54,7 +54,6 @@ import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -106,22 +105,11 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess
log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType()); log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType());
if (matchesClearRule(trigger, triggerConfig)) { if (matchesClearRule(trigger, triggerConfig)) {
List<NotificationRequest> notificationRequests = findAlreadySentNotificationRequests(rule, trigger); List<NotificationRequest> scheduledRequests = notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityIdAndStatus(
if (notificationRequests.isEmpty()) { rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId(), NotificationRequestStatus.SCHEDULED
return; );
} scheduledRequests.forEach(notificationRequest -> {
notificationCenter.deleteNotificationRequest(rule.getTenantId(), notificationRequest.getId());
List<UUID> targets = notificationRequests.stream()
.filter(NotificationRequest::isSent)
.flatMap(notificationRequest -> notificationRequest.getTargets().stream())
.distinct().collect(Collectors.toList());
NotificationInfo notificationInfo = constructNotificationInfo(trigger, triggerConfig);
submitNotificationRequest(targets, rule, trigger.getOriginatorEntityId(), notificationInfo, 0);
notificationRequests.forEach(notificationRequest -> {
if (notificationRequest.isScheduled()) {
notificationCenter.deleteNotificationRequest(rule.getTenantId(), notificationRequest.getId());
}
}); });
return; return;
} }
@ -139,10 +127,6 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess
} }
} }
private List<NotificationRequest> findAlreadySentNotificationRequests(NotificationRule rule, NotificationRuleTrigger trigger) {
return notificationRequestService.findNotificationRequestsByRuleIdAndOriginatorEntityId(rule.getTenantId(), rule.getId(), trigger.getOriginatorEntityId());
}
private void submitNotificationRequest(List<UUID> targets, NotificationRule rule, private void submitNotificationRequest(List<UUID> targets, NotificationRule rule,
EntityId originatorEntityId, NotificationInfo notificationInfo, int delayInSec) { EntityId originatorEntityId, NotificationInfo notificationInfo, int delayInSec) {
NotificationRequestConfig config = new NotificationRequestConfig(); NotificationRequestConfig config = new NotificationRequestConfig();

4
application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java

@ -46,10 +46,10 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor<A
if (triggerConfig.getNotifyOn().contains(AlarmAction.CREATED)) { if (triggerConfig.getNotifyOn().contains(AlarmAction.CREATED)) {
return severityMatches(alarm, triggerConfig); return severityMatches(alarm, triggerConfig);
} }
} else if (alarmUpdate.isSeverityChanged()) { } else if (alarmUpdate.isSeverityChanged()) {
if (triggerConfig.getNotifyOn().contains(AlarmAction.SEVERITY_CHANGED)) { if (triggerConfig.getNotifyOn().contains(AlarmAction.SEVERITY_CHANGED)) {
return severityMatches(alarmUpdate.getOld(), triggerConfig) || severityMatches(alarm, triggerConfig); return severityMatches(alarmUpdate.getOld(), triggerConfig) || severityMatches(alarm, triggerConfig);
} else { } else {
// if we haven't yet sent notification about the alarm // if we haven't yet sent notification about the alarm
return !severityMatches(alarmUpdate.getOld(), triggerConfig) && severityMatches(alarm, triggerConfig); return !severityMatches(alarmUpdate.getOld(), triggerConfig) && severityMatches(alarm, triggerConfig);
} }

1
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java

@ -34,7 +34,6 @@ public class AssetProfileExportService extends BaseEntityExportService<AssetProf
assetProfile.setDefaultDashboardId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultDashboardId())); assetProfile.setDefaultDashboardId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultDashboardId()));
assetProfile.setDefaultRuleChainId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultRuleChainId())); assetProfile.setDefaultRuleChainId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultRuleChainId()));
assetProfile.setDefaultEdgeRuleChainId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultEdgeRuleChainId())); assetProfile.setDefaultEdgeRuleChainId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultEdgeRuleChainId()));
imageService.inlineImage(assetProfile);
} }
@Override @Override

3
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java

@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.sync.ie.AttributeExportData;
import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.common.data.sync.ie.EntityExportData;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.resource.ImageService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.sync.ie.exporting.EntityExportService; import org.thingsboard.server.service.sync.ie.exporting.EntityExportService;
import org.thingsboard.server.service.sync.ie.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.ie.exporting.ExportableEntitiesService;
@ -60,8 +59,6 @@ public class DefaultEntityExportService<I extends EntityId, E extends Exportable
private RelationDao relationDao; private RelationDao relationDao;
@Autowired @Autowired
private AttributesService attributesService; private AttributesService attributesService;
@Autowired
protected ImageService imageService;
@Override @Override
public final D getExportData(EntitiesExportCtx<?> ctx, I entityId) throws ThingsboardException { public final D getExportData(EntitiesExportCtx<?> ctx, I entityId) throws ThingsboardException {

1
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java

@ -34,7 +34,6 @@ public class DeviceProfileExportService extends BaseEntityExportService<DevicePr
deviceProfile.setDefaultDashboardId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultDashboardId())); deviceProfile.setDefaultDashboardId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultDashboardId()));
deviceProfile.setDefaultRuleChainId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultRuleChainId())); deviceProfile.setDefaultRuleChainId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultRuleChainId()));
deviceProfile.setDefaultEdgeRuleChainId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultEdgeRuleChainId())); deviceProfile.setDefaultEdgeRuleChainId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultEdgeRuleChainId()));
imageService.inlineImage(deviceProfile);
} }
@Override @Override

1
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java

@ -40,7 +40,6 @@ public class WidgetsBundleExportService extends BaseEntityExportService<WidgetsB
if (widgetsBundle.getTenantId() == null || widgetsBundle.getTenantId().isNullUid()) { if (widgetsBundle.getTenantId() == null || widgetsBundle.getTenantId().isNullUid()) {
throw new IllegalArgumentException("Export of system Widget Bundles is not allowed"); throw new IllegalArgumentException("Export of system Widget Bundles is not allowed");
} }
imageService.inlineImage(widgetsBundle);
List<String> fqns = widgetTypeService.findWidgetFqnsByWidgetsBundleId(ctx.getTenantId(), widgetsBundle.getId()); List<String> fqns = widgetTypeService.findWidgetFqnsByWidgetsBundleId(ctx.getTenantId(), widgetsBundle.getId());
exportData.setFqns(fqns); exportData.setFqns(fqns);

6
application/src/main/resources/thingsboard.yml

@ -1446,11 +1446,11 @@ edges:
# Max number of high priority edge events per edge session. No persistence - stored in memory # Max number of high priority edge events per edge session. No persistence - stored in memory
max_high_priority_queue_size_per_session: "${EDGES_MAX_HIGH_PRIORITY_QUEUE_SIZE_PER_SESSION:10000}" max_high_priority_queue_size_per_session: "${EDGES_MAX_HIGH_PRIORITY_QUEUE_SIZE_PER_SESSION:10000}"
# Number of threads that are used to check DB for edge events # Number of threads that are used to check DB for edge events
scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:1}" scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:4}"
# Number of threads that are used to send downlink messages to edge over gRPC # Number of threads that are used to send downlink messages to edge over gRPC
send_scheduler_pool_size: "${EDGES_SEND_SCHEDULER_POOL_SIZE:1}" send_scheduler_pool_size: "${EDGES_SEND_SCHEDULER_POOL_SIZE:4}"
# Number of threads that are used to convert edge events from DB into downlink messages and send them for delivery # Number of threads that are used to convert edge events from DB into downlink messages and send them for delivery
grpc_callback_thread_pool_size: "${EDGES_GRPC_CALLBACK_POOL_SIZE:1}" grpc_callback_thread_pool_size: "${EDGES_GRPC_CALLBACK_POOL_SIZE:4}"
state: state:
# Persist state of edge (active, last connect, last disconnect) into timeseries or attributes tables. 'false' means to store edge state into attributes table # Persist state of edge (active, last connect, last disconnect) into timeseries or attributes tables. 'false' means to store edge state into attributes table
persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}" persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}"

3
application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java

@ -83,6 +83,7 @@ public class AuthControllerTest extends AbstractControllerTest {
assertThat(user.getEmail()).isEqualTo(CUSTOMER_USER_EMAIL); assertThat(user.getEmail()).isEqualTo(CUSTOMER_USER_EMAIL);
user = getUser(customerUserId); user = getUser(customerUserId);
assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue(); assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue();
assertThat(user.getAdditionalInfo().get("userActivated").asBoolean()).isTrue();
assertThat(user.getAdditionalInfo().get("lastLoginTs").asLong()).isCloseTo(System.currentTimeMillis(), within(10000L)); assertThat(user.getAdditionalInfo().get("lastLoginTs").asLong()).isCloseTo(System.currentTimeMillis(), within(10000L));
} }
@ -243,6 +244,7 @@ public class AuthControllerTest extends AbstractControllerTest {
user.setAuthority(Authority.TENANT_ADMIN); user.setAuthority(Authority.TENANT_ADMIN);
user.setEmail("tenant-admin-2@thingsboard.org"); user.setEmail("tenant-admin-2@thingsboard.org");
user = doPost("/api/user", user, User.class); user = doPost("/api/user", user, User.class);
assertThat(getUser(user.getId()).getAdditionalInfo().get("userActivated").asBoolean()).isFalse();
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, user.getUuidId()); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, user.getUuidId());
assertThat(userCredentials.getActivateTokenExpTime()).isCloseTo(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(ttl), Offset.offset(120000L)); assertThat(userCredentials.getActivateTokenExpTime()).isCloseTo(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(ttl), Offset.offset(120000L));
@ -289,6 +291,7 @@ public class AuthControllerTest extends AbstractControllerTest {
doPost("/api/noauth/activate", JacksonUtil.newObjectNode() doPost("/api/noauth/activate", JacksonUtil.newObjectNode()
.put("activateToken", newActivationToken) .put("activateToken", newActivationToken)
.put("password", "wefewe")).andExpect(status().isOk()); .put("password", "wefewe")).andExpect(status().isOk());
assertThat(getUser(user.getId()).getAdditionalInfo().get("userActivated").asBoolean()).isTrue();
} }
@Test @Test

19
application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java

@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ImageDescriptor;
import org.thingsboard.server.common.data.ResourceExportData; import org.thingsboard.server.common.data.ResourceExportData;
import org.thingsboard.server.common.data.ResourceSubType; import org.thingsboard.server.common.data.ResourceSubType;
@ -321,6 +322,24 @@ public class ImageControllerTest extends AbstractControllerTest {
assertThat(systemParams.getMaxResourceSize()).isEqualTo(0); assertThat(systemParams.getMaxResourceSize()).isEqualTo(0);
} }
@Test
public void testInlineImages() throws Exception {
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", "my_png_image.png", "image/png", PNG_IMAGE);
DeviceProfile deviceProfile = createDeviceProfile("Test");
deviceProfile.setImage("tb-image;" + imageInfo.getLink());
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
DeviceProfile deviceProfileInlined = doGet("/api/deviceProfile/" + deviceProfile.getUuidId() + "?inlineImages=true", DeviceProfile.class);
assertThat(deviceProfileInlined.getImage()).isEqualTo("tb-image:" +
Base64.getEncoder().encodeToString(imageInfo.getResourceKey().getBytes()) + ":"
+ Base64.getEncoder().encodeToString(imageInfo.getName().getBytes()) + ":"
+ Base64.getEncoder().encodeToString(imageInfo.getResourceSubType().name().getBytes()) +
";data:image/png;base64," + Base64.getEncoder().encodeToString(PNG_IMAGE));
deviceProfile = doGet("/api/deviceProfile/" + deviceProfile.getUuidId(), DeviceProfile.class);
assertThat(deviceProfile.getImage()).isEqualTo("tb-image;" + imageInfo.getLink());
}
private TbResourceInfo updateImagePublicStatus(String filename, boolean isPublic) throws Exception { private TbResourceInfo updateImagePublicStatus(String filename, boolean isPublic) throws Exception {
return doPut("/api/images/tenant/" + filename + "/public/" + isPublic, "", TbResourceInfo.class); return doPut("/api/images/tenant/" + filename + "/public/" + isPublic, "", TbResourceInfo.class);
} }

8
application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java

@ -274,7 +274,6 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
Device device = createDevice("Device with alarm", "233"); Device device = createDevice("Device with alarm", "233");
NotificationTarget target = createNotificationTarget(tenantAdminUserId); NotificationTarget target = createNotificationTarget(tenantAdminUserId);
defaultNotifications.create(tenantId, DefaultNotifications.newAlarm, target.getId()); defaultNotifications.create(tenantId, DefaultNotifications.newAlarm, target.getId());
defaultNotifications.create(tenantId, DefaultNotifications.entityAction, target.getId());
notificationRulesCache.evict(tenantId); notificationRulesCache.evict(tenantId);
Alarm alarm = new Alarm(); Alarm alarm = new Alarm();
@ -287,7 +286,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.pollDelay(2, TimeUnit.SECONDS) .pollDelay(2, TimeUnit.SECONDS)
.untilAsserted(() -> { .untilAsserted(() -> {
List<Notification> notifications = getMyNotifications(false, 10); List<Notification> notifications = getMyNotifications(false, 10);
assertThat(notifications).hasSize(1).first().matches(notification -> { assertThat(notifications).singleElement().matches(notification -> {
return notification.getType() == NotificationType.ALARM && return notification.getType() == NotificationType.ALARM &&
notification.getSubject().equals("New alarm 'testAlarm'"); notification.getSubject().equals("New alarm 'testAlarm'");
}); });
@ -353,12 +352,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.findFirst().orElse(null); .findFirst().orElse(null);
assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo()); assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo());
getWsClient().registerWaitForUpdate();
alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null); alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null);
getWsClient().waitForUpdate(true);
notification = getWsClient().getLastDataUpdate().getUpdate();
assertThat(notification.getSubject()).isEqualTo("critical alarm '" + alarmType + "' is CLEARED_UNACK");
await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty(); assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty();
}); });

2
common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestService.java

@ -43,7 +43,7 @@ public interface NotificationRequestService {
List<NotificationRequestId> findNotificationRequestsIdsByStatusAndRuleId(TenantId tenantId, NotificationRequestStatus requestStatus, NotificationRuleId ruleId); List<NotificationRequestId> findNotificationRequestsIdsByStatusAndRuleId(TenantId tenantId, NotificationRequestStatus requestStatus, NotificationRuleId ruleId);
List<NotificationRequest> findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId); List<NotificationRequest> findNotificationRequestsByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status);
void deleteNotificationRequest(TenantId tenantId, NotificationRequest request); void deleteNotificationRequest(TenantId tenantId, NotificationRequest request);

2
common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java

@ -64,7 +64,7 @@ public interface ImageService {
boolean updateImagesUsage(WidgetTypeDetails widgetType); boolean updateImagesUsage(WidgetTypeDetails widgetType);
void inlineImage(HasImage entity); <T extends HasImage> T inlineImage(T entity);
Collection<TbResourceInfo> getUsedImages(Dashboard dashboard); Collection<TbResourceInfo> getUsedImages(Dashboard dashboard);

13
common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java

@ -51,7 +51,9 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.FetchResult; import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.SshTransport; import org.eclipse.jgit.transport.SshTransport;
import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
@ -85,6 +87,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
@ -350,8 +353,16 @@ public class GitRepository {
return; return;
} }
log.debug("Executing push [{}][{}]", settings.getRepositoryUri(), remoteBranch); log.debug("Executing push [{}][{}]", settings.getRepositoryUri(), remoteBranch);
execute(git.push() Iterable<PushResult> result = execute(git.push()
.setRefSpecs(new RefSpec(localBranch + ":" + remoteBranch))); .setRefSpecs(new RefSpec(localBranch + ":" + remoteBranch)));
result.forEach(pushResult -> {
for (RemoteRefUpdate update : pushResult.getRemoteUpdates()) {
RemoteRefUpdate.Status status = update.getStatus();
if (status != RemoteRefUpdate.Status.OK && status != RemoteRefUpdate.Status.UP_TO_DATE) {
throw new RuntimeException("Failed to push changes: " + Optional.ofNullable(update.getMessage()).orElseGet(status::name));
}
}
});
} }
public String getContentsDiff(String content1, String content2) throws IOException { public String getContentsDiff(String content1, String content2) throws IOException {

2
dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java

@ -67,7 +67,7 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe
return savedDomain; return savedDomain;
} catch (Exception e) { } catch (Exception e) {
checkConstraintViolation(e, checkConstraintViolation(e,
Map.of("domain_unq_key", "Domain with such name and scheme already exists!")); Map.of("domain_name_key", "Domain with such name and scheme already exists!"));
throw e; throw e;
} }
} }

4
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java

@ -82,8 +82,8 @@ public class DefaultNotificationRequestService implements NotificationRequestSer
} }
@Override @Override
public List<NotificationRequest> findNotificationRequestsByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId) { public List<NotificationRequest> findNotificationRequestsByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status) {
return notificationRequestDao.findByRuleIdAndOriginatorEntityId(tenantId, ruleId, originatorEntityId); return notificationRequestDao.findByRuleIdAndOriginatorEntityIdAndStatus(tenantId, ruleId, originatorEntityId, status);
} }
@Override @Override

2
dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRequestDao.java

@ -40,7 +40,7 @@ public interface NotificationRequestDao extends Dao<NotificationRequest> {
List<NotificationRequestId> findIdsByRuleId(TenantId tenantId, NotificationRequestStatus requestStatus, NotificationRuleId ruleId); List<NotificationRequestId> findIdsByRuleId(TenantId tenantId, NotificationRequestStatus requestStatus, NotificationRuleId ruleId);
List<NotificationRequest> findByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId); List<NotificationRequest> findByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status);
PageData<NotificationRequest> findAllByStatus(NotificationRequestStatus status, PageLink pageLink); PageData<NotificationRequest> findAllByStatus(NotificationRequestStatus status, PageLink pageLink);

10
dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java

@ -27,6 +27,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
@ -526,9 +527,16 @@ public class BaseImageService extends BaseResourceService implements ImageServic
} }
@Override @Override
public void inlineImage(HasImage entity) { public <T extends HasImage> T inlineImage(T entity) {
log.trace("Executing inlineImage [{}] [{}] [{}]", entity.getTenantId(), entity.getClass().getSimpleName(), entity.getName()); log.trace("Executing inlineImage [{}] [{}] [{}]", entity.getTenantId(), entity.getClass().getSimpleName(), entity.getName());
if (StringUtils.isEmpty(entity.getImage())) {
return entity;
}
if (cache instanceof CaffeineTbTransactionalCache) {
entity = JacksonUtil.clone(entity); // cloning the entity to avoid updating the cached one
}
entity.setImage(inlineImage(entity.getTenantId(), "image", entity.getImage(), true)); entity.setImage(inlineImage(entity.getTenantId(), "image", entity.getImage(), true));
return entity;
} }
@Override @Override

4
dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java

@ -73,8 +73,8 @@ public class JpaNotificationRequestDao extends JpaAbstractDao<NotificationReques
} }
@Override @Override
public List<NotificationRequest> findByRuleIdAndOriginatorEntityId(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId) { public List<NotificationRequest> findByRuleIdAndOriginatorEntityIdAndStatus(TenantId tenantId, NotificationRuleId ruleId, EntityId originatorEntityId, NotificationRequestStatus status) {
return DaoUtil.convertDataList(notificationRequestRepository.findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityType(ruleId.getId(), originatorEntityId.getId(), originatorEntityId.getEntityType())); return DaoUtil.convertDataList(notificationRequestRepository.findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityTypeAndStatus(ruleId.getId(), originatorEntityId.getId(), originatorEntityId.getEntityType(), status));
} }
@Override @Override

2
dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java

@ -54,7 +54,7 @@ public interface NotificationRequestRepository extends JpaRepository<Notificatio
List<UUID> findAllIdsByStatusAndRuleId(@Param("status") NotificationRequestStatus status, List<UUID> findAllIdsByStatusAndRuleId(@Param("status") NotificationRequestStatus status,
@Param("ruleId") UUID ruleId); @Param("ruleId") UUID ruleId);
List<NotificationRequestEntity> findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityType(UUID ruleId, UUID originatorEntityId, EntityType originatorEntityType); List<NotificationRequestEntity> findAllByRuleIdAndOriginatorEntityIdAndOriginatorEntityTypeAndStatus(UUID ruleId, UUID originatorEntityId, EntityType originatorEntityType, NotificationRequestStatus status);
Page<NotificationRequestEntity> findAllByStatus(NotificationRequestStatus status, Pageable pageable); Page<NotificationRequestEntity> findAllByStatus(NotificationRequestStatus status, Pageable pageable);

23
pom.xml

@ -869,6 +869,29 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>5.0.0</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<!-- <includeOnlyProperties>-->
<!-- <includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>-->
<!-- <includeOnlyProperty>^git.commit.id.(abbrev|full)$</includeOnlyProperty>-->
<!-- </includeOnlyProperties>-->
<commitIdGenerationMode>full</commitIdGenerationMode>
</configuration>
</plugin>
</plugins> </plugins>
</pluginManagement> </pluginManagement>
<plugins> <plugins>

9
ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts

@ -16,6 +16,7 @@
import { import {
Component, Component,
DestroyRef,
ElementRef, ElementRef,
forwardRef, forwardRef,
Inject, Inject,
@ -46,6 +47,7 @@ import { EntityType } from '@shared/models/entity-type.models';
import { fromEvent, Subscription } from 'rxjs'; import { fromEvent, Subscription } from 'rxjs';
import { POSITION_MAP } from '@shared/models/overlay.models'; import { POSITION_MAP } from '@shared/models/overlay.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const ALARM_FILTER_CONFIG_DATA = new InjectionToken<any>('AlarmFilterConfigData'); export const ALARM_FILTER_CONFIG_DATA = new InjectionToken<any>('AlarmFilterConfigData');
@ -129,7 +131,8 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal
private overlay: Overlay, private overlay: Overlay,
private nativeElement: ElementRef, private nativeElement: ElementRef,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
private utils: UtilsService) { private utils: UtilsService,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -149,7 +152,9 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal
searchPropagatedAlarms: [false, []], searchPropagatedAlarms: [false, []],
assigneeId: [AlarmAssigneeOption.noAssignee, []] assigneeId: [AlarmAssigneeOption.noAssignee, []]
}); });
this.alarmFilterConfigForm.valueChanges.subscribe( this.alarmFilterConfigForm.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
if (!this.buttonMode) { if (!this.buttonMode) {
this.alarmConfigUpdated(this.alarmFilterConfigForm.value); this.alarmConfigUpdated(this.alarmFilterConfigForm.value);

10
ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { Component, DestroyRef, Inject, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -40,6 +40,7 @@ import { DialogService } from '@core/services/dialog.service';
import { deepClone, isUndefined } from '@core/utils'; import { deepClone, isUndefined } from '@core/utils';
import { EntityAliasDialogComponent, EntityAliasDialogData } from './entity-alias-dialog.component'; import { EntityAliasDialogComponent, EntityAliasDialogData } from './entity-alias-dialog.component';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface EntityAliasesDialogData { export interface EntityAliasesDialogData {
entityAliases: EntityAliases; entityAliases: EntityAliases;
@ -80,7 +81,8 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD
private dashboardUtils: DashboardUtilsService, private dashboardUtils: DashboardUtilsService,
private translate: TranslateService, private translate: TranslateService,
private dialogs: DialogService, private dialogs: DialogService,
private dialog: MatDialog) { private dialog: MatDialog,
private destroyRef: DestroyRef) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.title = data.customTitle ? data.customTitle : 'entity.aliases'; this.title = data.customTitle ? data.customTitle : 'entity.aliases';
this.disableAdd = this.data.disableAdd; this.disableAdd = this.data.disableAdd;
@ -151,7 +153,9 @@ export class EntityAliasesDialogComponent extends DialogComponent<EntityAliasesD
filter: [entityAlias ? entityAlias.filter : null], filter: [entityAlias ? entityAlias.filter : null],
resolveMultiple: [entityAlias ? entityAlias.filter.resolveMultiple : false] resolveMultiple: [entityAlias ? entityAlias.filter.resolveMultiple : false]
}); });
aliasFormControl.get('resolveMultiple').valueChanges.subscribe((resolveMultiple: boolean) => { aliasFormControl.get('resolveMultiple').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((resolveMultiple: boolean) => {
(aliasFormControl.get('filter').value as EntityAliasFilter).resolveMultiple = resolveMultiple; (aliasFormControl.get('filter').value as EntityAliasFilter).resolveMultiple = resolveMultiple;
}); });
return aliasFormControl; return aliasFormControl;

5
ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.ts

@ -40,6 +40,7 @@ import { AliasesInfo } from '@shared/models/alias.models';
import { ItemBufferService } from '@core/services/item-buffer.service'; import { ItemBufferService } from '@core/services/item-buffer.service';
import { StateObject } from '@core/api/widget-api.models'; import { StateObject } from '@core/api/widget-api.models';
import { FiltersInfo } from '@shared/models/query/query.models'; import { FiltersInfo } from '@shared/models/query/query.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface AddWidgetToDashboardDialogData { export interface AddWidgetToDashboardDialogData {
entityId: EntityId; entityId: EntityId;
@ -83,7 +84,9 @@ export class AddWidgetToDashboardDialogComponent extends
} }
); );
this.addWidgetFormGroup.get('addToDashboardType').valueChanges.subscribe( this.addWidgetFormGroup.get('addToDashboardType').valueChanges.pipe(
takeUntilDestroyed()
).subscribe(
(addToDashboardType: number) => { (addToDashboardType: number) => {
if (addToDashboardType === 0) { if (addToDashboardType === 0) {
this.addWidgetFormGroup.get('dashboardId').setValidators([Validators.required]); this.addWidgetFormGroup.get('dashboardId').setValidators([Validators.required]);

5
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-image-dialog.component.ts

@ -28,6 +28,7 @@ import html2canvas from 'html2canvas';
import { map, share } from 'rxjs/operators'; import { map, share } from 'rxjs/operators';
import { BehaviorSubject, from } from 'rxjs'; import { BehaviorSubject, from } from 'rxjs';
import { isNumber } from '@core/utils'; import { isNumber } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface DashboardImageDialogData { export interface DashboardImageDialogData {
dashboardId: DashboardId; dashboardId: DashboardId;
@ -84,7 +85,9 @@ export class DashboardImageDialogComponent extends DialogComponent<DashboardImag
dashboardImage: [this.data.currentImage] dashboardImage: [this.data.currentImage]
}); });
this.dashboardImageFormGroup.get('dashboardImage').valueChanges.subscribe( this.dashboardImageFormGroup.get('dashboardImage').valueChanges.pipe(
takeUntilDestroyed()
).subscribe(
(newImage) => { (newImage) => {
this.updateImage(newImage); this.updateImage(newImage);
} }

9
ui-ngx/src/app/modules/home/components/dashboard-page/states/dashboard-state-dialog.component.ts

@ -34,6 +34,7 @@ import { DashboardState } from '@app/shared/models/dashboard.models';
import { DashboardStateInfo } from '@home/components/dashboard-page/states/manage-dashboard-states-dialog.component.models'; import { DashboardStateInfo } from '@home/components/dashboard-page/states/manage-dashboard-states-dialog.component.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface DashboardStateDialogData { export interface DashboardStateDialogData {
states: {[id: string]: DashboardState }; states: {[id: string]: DashboardState };
@ -89,11 +90,15 @@ export class DashboardStateDialogComponent extends
root: [this.state.root, []], root: [this.state.root, []],
}); });
this.stateFormGroup.get('name').valueChanges.subscribe((name: string) => { this.stateFormGroup.get('name').valueChanges.pipe(
takeUntilDestroyed()
).subscribe((name: string) => {
this.checkStateName(name); this.checkStateName(name);
}); });
this.stateFormGroup.get('id').valueChanges.subscribe((id: string) => { this.stateFormGroup.get('id').valueChanges.pipe(
takeUntilDestroyed()
).subscribe((id: string) => {
this.stateIdTouched = true; this.stateIdTouched = true;
}); });
} }

9
ui-ngx/src/app/modules/home/components/device/device-info-filter.component.ts

@ -17,6 +17,7 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
ElementRef, ElementRef,
forwardRef, forwardRef,
Inject, Inject,
@ -38,6 +39,7 @@ import { DeviceInfoFilter } from '@shared/models/device.models';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { EntityInfoData } from '@shared/models/entity.models'; import { EntityInfoData } from '@shared/models/entity.models';
import { DeviceProfileService } from '@core/http/device-profile.service'; import { DeviceProfileService } from '@core/http/device-profile.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const DEVICE_FILTER_CONFIG_DATA = new InjectionToken<any>('DeviceFilterConfigData'); export const DEVICE_FILTER_CONFIG_DATA = new InjectionToken<any>('DeviceFilterConfigData');
@ -96,7 +98,8 @@ export class DeviceInfoFilterComponent implements OnInit, OnDestroy, ControlValu
private nativeElement: ElementRef, private nativeElement: ElementRef,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
private deviceProfileService: DeviceProfileService, private deviceProfileService: DeviceProfileService,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -108,7 +111,9 @@ export class DeviceInfoFilterComponent implements OnInit, OnDestroy, ControlValu
deviceProfileId: [null, []], deviceProfileId: [null, []],
active: ['', []] active: ['', []]
}); });
this.deviceInfoFilterForm.valueChanges.subscribe( this.deviceInfoFilterForm.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.updateValidators(); this.updateValidators();
if (!this.buttonMode) { if (!this.buttonMode) {

9
ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts

@ -18,15 +18,18 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms';
import { ContactBased } from '@shared/models/contact-based.model'; import { ContactBased } from '@shared/models/contact-based.model';
import { AfterViewInit, ChangeDetectorRef, Directive } from '@angular/core'; import { AfterViewInit, ChangeDetectorRef, DestroyRef, Directive, inject } from '@angular/core';
import { HasId } from '@shared/models/base-data'; import { HasId } from '@shared/models/base-data';
import { EntityComponent } from './entity.component'; import { EntityComponent } from './entity.component';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { CountryData } from '@shared/models/country.models'; import { CountryData } from '@shared/models/country.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Directive() @Directive()
export abstract class ContactBasedComponent<T extends ContactBased<HasId>> extends EntityComponent<T> implements AfterViewInit { export abstract class ContactBasedComponent<T extends ContactBased<HasId>> extends EntityComponent<T> implements AfterViewInit {
protected destroyRef = inject(DestroyRef);
protected constructor(protected store: Store<AppState>, protected constructor(protected store: Store<AppState>,
protected fb: UntypedFormBuilder, protected fb: UntypedFormBuilder,
protected entityValue: T, protected entityValue: T,
@ -65,7 +68,9 @@ export abstract class ContactBasedComponent<T extends ContactBased<HasId>> exten
} }
ngAfterViewInit() { ngAfterViewInit() {
this.entityForm.get('country').valueChanges.subscribe( this.entityForm.get('country').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
(country) => { (country) => {
this.entityForm.get('zip').setValidators(this.zipValidators(country)); this.entityForm.get('zip').setValidators(this.zipValidators(country));
this.entityForm.get('zip').updateValueAndValidity({onlySelf: true}); this.entityForm.get('zip').updateValueAndValidity({onlySelf: true});

10
ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -32,6 +32,7 @@ import {
EntityKeyValueType, EntityKeyValueType,
FilterPredicateType FilterPredicateType
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-boolean-filter-predicate', selector: 'tb-boolean-filter-predicate',
@ -68,7 +69,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, Va
private propagateChange = null; private propagateChange = null;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -76,7 +78,9 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, Va
operation: [BooleanOperation.EQUAL, [Validators.required]], operation: [BooleanOperation.EQUAL, [Validators.required]],
value: [null, [Validators.required]] value: [null, [Validators.required]]
}); });
this.booleanFilterPredicateFormGroup.valueChanges.subscribe(() => { this.booleanFilterPredicateFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

14
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -35,6 +35,7 @@ import {
inheritModeForDynamicValueSourceType, inheritModeForDynamicValueSourceType,
StringOperation StringOperation
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-filter-predicate-value', selector: 'tb-filter-predicate-value',
@ -113,7 +114,8 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, Vali
private propagateChange = null; private propagateChange = null;
private propagateChangePending = false; private propagateChangePending = false;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -147,7 +149,9 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, Vali
} }
) )
}); });
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.subscribe( this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
(sourceType) => { (sourceType) => {
if (!sourceType) { if (!sourceType) {
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false}); this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false});
@ -156,7 +160,9 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, Vali
} }
); );
this.updateValidationDynamicMode(); this.updateValidationDynamicMode();
this.filterPredicateValueFormGroup.valueChanges.subscribe(() => { this.filterPredicateValueFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -26,6 +26,7 @@ import {
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { EntityKeyValueType, FilterPredicateType, KeyFilterPredicateInfo } from '@shared/models/query/query.models'; import { EntityKeyValueType, FilterPredicateType, KeyFilterPredicateInfo } from '@shared/models/query/query.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-filter-predicate', selector: 'tb-filter-predicate',
@ -66,7 +67,8 @@ export class FilterPredicateComponent implements ControlValueAccessor, Validator
private propagateChange = null; private propagateChange = null;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -74,7 +76,9 @@ export class FilterPredicateComponent implements ControlValueAccessor, Validator
predicate: [null, [Validators.required]], predicate: [null, [Validators.required]],
userInfo: [null, []] userInfo: [null, []]
}); });
this.filterPredicateFormGroup.valueChanges.subscribe(() => { this.filterPredicateFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

5
ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.ts

@ -29,6 +29,7 @@ import {
StringOperation StringOperation
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface FilterUserInfoDialogData { export interface FilterUserInfoDialogData {
key: string; key: string;
@ -73,7 +74,9 @@ export class FilterUserInfoDialogComponent extends
); );
this.onAutogeneratedLabelChange(); this.onAutogeneratedLabelChange();
if (!this.data.readonly) { if (!this.data.readonly) {
this.filterUserInfoFormGroup.get('autogeneratedLabel').valueChanges.subscribe(() => { this.filterUserInfoFormGroup.get('autogeneratedLabel').valueChanges.pipe(
takeUntilDestroyed()
).subscribe(() => {
this.onAutogeneratedLabelChange(); this.onAutogeneratedLabelChange();
}); });
} else { } else {

10
ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -32,6 +32,7 @@ import {
NumericOperation, NumericOperation,
numericOperationTranslationMap, numericOperationTranslationMap,
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-numeric-filter-predicate', selector: 'tb-numeric-filter-predicate',
@ -70,7 +71,8 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, Va
private propagateChange = null; private propagateChange = null;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -78,7 +80,9 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, Va
operation: [NumericOperation.EQUAL, [Validators.required]], operation: [NumericOperation.EQUAL, [Validators.required]],
value: [null, [Validators.required]] value: [null, [Validators.required]]
}); });
this.numericFilterPredicateFormGroup.valueChanges.subscribe(() => { this.numericFilterPredicateFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -32,6 +32,7 @@ import {
StringOperation, StringOperation,
stringOperationTranslationMap stringOperationTranslationMap
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-string-filter-predicate', selector: 'tb-string-filter-predicate',
@ -68,7 +69,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, Val
private propagateChange = null; private propagateChange = null;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -77,7 +79,9 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, Val
value: [null, [Validators.required]], value: [null, [Validators.required]],
ignoreCase: [false] ignoreCase: [false]
}); });
this.stringFilterPredicateFormGroup.valueChanges.subscribe(() => { this.stringFilterPredicateFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { Component, DestroyRef, Inject, OnInit, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -38,6 +38,7 @@ import {
UserFilterInputInfo UserFilterInputInfo
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface UserFilterDialogData { export interface UserFilterDialogData {
filter: Filter; filter: Filter;
@ -66,7 +67,8 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<UserFilterDialogComponent, Filter>, public dialogRef: MatDialogRef<UserFilterDialogComponent, Filter>,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
public translate: TranslateService) { public translate: TranslateService,
private destroyRef: DestroyRef) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.filter = data.filter; this.filter = data.filter;
const userInputs = filterToUserFilterInfoList(this.filter, translate); const userInputs = filterToUserFilterInfoList(this.filter, translate);
@ -91,7 +93,9 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC
userInput.valueType === EntityKeyValueType.NUMERIC || userInput.valueType === EntityKeyValueType.NUMERIC ||
userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []] userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []]
}); });
userInputControl.get('value').valueChanges.subscribe(userValue => { userInputControl.get('value').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(userValue => {
(userInput.info.keyFilterPredicate as any).value.userValue = userValue; (userInput.info.keyFilterPredicate as any).value.userValue = userValue;
}); });
return userInputControl; return userInputControl;

5
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -46,6 +46,7 @@ import { Observable } from 'rxjs';
import { BreakpointObserver } from '@angular/cdk/layout'; import { BreakpointObserver } from '@angular/cdk/layout';
import { MediaBreakpoints } from '@shared/models/constants'; import { MediaBreakpoints } from '@shared/models/constants';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface AddDeviceProfileDialogData { export interface AddDeviceProfileDialogData {
deviceProfileName: string; deviceProfileName: string;
@ -125,7 +126,9 @@ export class AddDeviceProfileDialogComponent extends
[Validators.required]] [Validators.required]]
} }
); );
this.transportConfigFormGroup.get('transportType').valueChanges.subscribe(() => { this.transportConfigFormGroup.get('transportType').valueChanges.pipe(
takeUntilDestroyed()
).subscribe(() => {
this.deviceProfileTransportTypeChanged(); this.deviceProfileTransportTypeChanged();
}); });

14
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { import {
DynamicValueSourceType, DynamicValueSourceType,
@ -24,6 +24,7 @@ import {
inheritModeForDynamicValueSourceType inheritModeForDynamicValueSourceType
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { AlarmConditionType } from '@shared/models/device.models'; import { AlarmConditionType } from '@shared/models/device.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-alarm-duration-predicate-value', selector: 'tb-alarm-duration-predicate-value',
@ -76,7 +77,8 @@ export class AlarmDurationPredicateValueComponent implements ControlValueAccesso
private propagateChange = null; private propagateChange = null;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -90,7 +92,9 @@ export class AlarmDurationPredicateValueComponent implements ControlValueAccesso
} }
) )
}); });
this.alarmDurationPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.subscribe( this.alarmDurationPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
(sourceType) => { (sourceType) => {
if (!sourceType) { if (!sourceType) {
this.alarmDurationPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false}); this.alarmDurationPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false});
@ -98,7 +102,9 @@ export class AlarmDurationPredicateValueComponent implements ControlValueAccesso
this.updateShowInheritMode(sourceType); this.updateShowInheritMode(sourceType);
} }
); );
this.alarmDurationPredicateValueFormGroup.valueChanges.subscribe(() => { this.alarmDurationPredicateValueFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

14
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-dynamic-value.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -26,6 +26,7 @@ import {
dynamicValueSourceTypeTranslationMap, dynamicValueSourceTypeTranslationMap,
getDynamicSourcesForAllowUser getDynamicSourcesForAllowUser
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-alarm-dynamic-value', selector: 'tb-alarm-dynamic-value',
@ -49,7 +50,8 @@ export class AlarmDynamicValue implements ControlValueAccessor, OnInit{
@Input() @Input()
disabled: boolean; disabled: boolean;
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -58,7 +60,9 @@ export class AlarmDynamicValue implements ControlValueAccessor, OnInit{
sourceAttribute: [null] sourceAttribute: [null]
}) })
this.dynamicValue.get('sourceType').valueChanges.subscribe( this.dynamicValue.get('sourceType').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
(sourceType) => { (sourceType) => {
if (!sourceType) { if (!sourceType) {
this.dynamicValue.get('sourceAttribute').patchValue(null, {emitEvent: false}); this.dynamicValue.get('sourceAttribute').patchValue(null, {emitEvent: false});
@ -66,7 +70,9 @@ export class AlarmDynamicValue implements ControlValueAccessor, OnInit{
} }
); );
this.dynamicValue.valueChanges.subscribe(() => { this.dynamicValue.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}) })
} }

5
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.ts

@ -27,6 +27,7 @@ import { keyFilterInfosToKeyFilters, keyFiltersToKeyFilterInfos } from '@shared/
import { AlarmCondition, AlarmConditionType, AlarmConditionTypeTranslationMap } from '@shared/models/device.models'; import { AlarmCondition, AlarmConditionType, AlarmConditionTypeTranslationMap } from '@shared/models/device.models';
import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface AlarmRuleConditionDialogData { export interface AlarmRuleConditionDialogData {
readonly: boolean; readonly: boolean;
@ -74,7 +75,9 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRule
}) })
}); });
this.conditionFormGroup.patchValue({spec: this.condition?.spec}); this.conditionFormGroup.patchValue({spec: this.condition?.spec});
this.conditionFormGroup.get('spec.type').valueChanges.subscribe((type) => { this.conditionFormGroup.get('spec.type').valueChanges.pipe(
takeUntilDestroyed()
).subscribe((type) => {
this.updateValidators(type, true, true); this.updateValidators(type, true, true);
}); });
if (this.readonly) { if (this.readonly) {

10
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -36,6 +36,7 @@ import {
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { DashboardId } from '@shared/models/id/dashboard-id'; import { DashboardId } from '@shared/models/id/dashboard-id';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-alarm-rule', selector: 'tb-alarm-rule',
@ -81,7 +82,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
constructor(private dialog: MatDialog, constructor(private dialog: MatDialog,
private utils: UtilsService, private utils: UtilsService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -98,7 +100,9 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
alarmDetails: [null], alarmDetails: [null],
dashboardId: [null] dashboardId: [null]
}); });
this.alarmRuleFormGroup.valueChanges.subscribe(() => { this.alarmRuleFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

14
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -40,6 +40,7 @@ import {
import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { isDefined, isDefinedAndNotNull } from '@core/utils';
import { MatCheckboxChange } from '@angular/material/checkbox'; import { MatCheckboxChange } from '@angular/material/checkbox';
import { getDefaultTimezone } from '@shared/models/time/time.models'; import { getDefaultTimezone } from '@shared/models/time/time.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-alarm-schedule', selector: 'tb-alarm-schedule',
@ -80,7 +81,8 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator,
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -94,13 +96,17 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator,
dynamicValue: [null] dynamicValue: [null]
}); });
this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { this.alarmScheduleForm.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((type) => {
const defaultTimezone = getDefaultTimezone(); const defaultTimezone = getDefaultTimezone();
this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false});
this.updateValidators(type, true); this.updateValidators(type, true);
this.alarmScheduleForm.updateValueAndValidity(); this.alarmScheduleForm.updateValueAndValidity();
}); });
this.alarmScheduleForm.valueChanges.subscribe(() => { this.alarmScheduleForm.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -31,6 +31,7 @@ import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
import { MatChipInputEvent } from '@angular/material/chips'; import { MatChipInputEvent } from '@angular/material/chips';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-device-profile-alarm', selector: 'tb-device-profile-alarm',
@ -74,7 +75,8 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
constructor(private dialog: MatDialog, constructor(private dialog: MatDialog,
private utils: UtilsService, private utils: UtilsService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -101,7 +103,9 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
propagateToOwner: [null], propagateToOwner: [null],
propagateToTenant: [null] propagateToTenant: [null]
}, { validators: deviceProfileAlarmValidator }); }, { validators: deviceProfileAlarmValidator });
this.alarmFormGroup.valueChanges.subscribe(() => { this.alarmFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

14
ui-ngx/src/app/modules/home/components/profile/device-profile-provision-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -37,6 +37,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions'
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-device-profile-provision-configuration', selector: 'tb-device-profile-provision-configuration',
@ -79,7 +80,8 @@ export class DeviceProfileProvisionConfigurationComponent implements ControlValu
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private translate: TranslateService) { private translate: TranslateService,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -91,7 +93,9 @@ export class DeviceProfileProvisionConfigurationComponent implements ControlValu
certificateRegExPattern: [{value: null, disabled: true}, Validators.required], certificateRegExPattern: [{value: null, disabled: true}, Validators.required],
allowCreateNewDevicesByX509Certificate: [{value: null, disabled: true}, Validators.required] allowCreateNewDevicesByX509Certificate: [{value: null, disabled: true}, Validators.required]
}); });
this.provisionConfigurationFormGroup.get('type').valueChanges.subscribe((type) => { this.provisionConfigurationFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((type) => {
if (type === DeviceProvisionType.DISABLED) { if (type === DeviceProvisionType.DISABLED) {
for (const field in this.provisionConfigurationFormGroup.controls) { for (const field in this.provisionConfigurationFormGroup.controls) {
if (field !== 'type') { if (field !== 'type') {
@ -129,7 +133,9 @@ export class DeviceProfileProvisionConfigurationComponent implements ControlValu
this.provisionConfigurationFormGroup.get('provisionDeviceKey').enable({emitEvent: false}); this.provisionConfigurationFormGroup.get('provisionDeviceKey').enable({emitEvent: false});
} }
}); });
this.provisionConfigurationFormGroup.valueChanges.subscribe(() => { this.provisionConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

14
ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, Inject, Input, Optional } from '@angular/core'; import { ChangeDetectorRef, Component, DestroyRef, Inject, Input, Optional } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
@ -43,6 +43,7 @@ import { EntityId } from '@shared/models/id/entity-id';
import { OtaUpdateType } from '@shared/models/ota-package.models'; import { OtaUpdateType } from '@shared/models/ota-package.models';
import { DashboardId } from '@shared/models/id/dashboard-id'; import { DashboardId } from '@shared/models/id/dashboard-id';
import { RuleChainType } from '@shared/models/rule-chain.models'; import { RuleChainType } from '@shared/models/rule-chain.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-device-profile', selector: 'tb-device-profile',
@ -83,7 +84,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
@Optional() @Inject('entity') protected entityValue: DeviceProfile, @Optional() @Inject('entity') protected entityValue: DeviceProfile,
@Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<DeviceProfile>, @Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<DeviceProfile>,
protected fb: UntypedFormBuilder, protected fb: UntypedFormBuilder,
protected cd: ChangeDetectorRef) { protected cd: ChangeDetectorRef,
private destroyRef: DestroyRef) {
super(store, fb, entityValue, entitiesTableConfigValue, cd); super(store, fb, entityValue, entitiesTableConfigValue, cd);
} }
@ -129,10 +131,14 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
description: [entity ? entity.description : '', []], description: [entity ? entity.description : '', []],
} }
); );
form.get('type').valueChanges.subscribe(() => { form.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.deviceProfileTypeChanged(form); this.deviceProfileTypeChanged(form);
}); });
form.get('transportType').valueChanges.subscribe(() => { form.get('transportType').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.deviceProfileTransportTypeChanged(form); this.deviceProfileTransportTypeChanged(form);
}); });
this.checkIsNewDeviceProfile(entity, form); this.checkIsNewDeviceProfile(entity, form);

10
ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
@ -23,6 +23,7 @@ import {
DeviceProfileConfiguration, DeviceProfileConfiguration,
DeviceProfileType DeviceProfileType
} from '@shared/models/device.models'; } from '@shared/models/device.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-default-device-profile-configuration', selector: 'tb-default-device-profile-configuration',
@ -44,7 +45,8 @@ export class DefaultDeviceProfileConfigurationComponent implements ControlValueA
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -58,7 +60,9 @@ export class DefaultDeviceProfileConfigurationComponent implements ControlValueA
this.defaultDeviceProfileConfigurationFormGroup = this.fb.group({ this.defaultDeviceProfileConfigurationFormGroup = this.fb.group({
configuration: [null, Validators.required] configuration: [null, Validators.required]
}); });
this.defaultDeviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { this.defaultDeviceProfileConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALIDATORS, NG_VALIDATORS,
@ -29,6 +29,7 @@ import {
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { DefaultDeviceProfileTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; import { DefaultDeviceProfileTransportConfiguration, DeviceTransportType } from '@shared/models/device.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-default-device-profile-transport-configuration', selector: 'tb-default-device-profile-transport-configuration',
@ -57,7 +58,8 @@ export class DefaultDeviceProfileTransportConfigurationComponent implements Cont
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -71,7 +73,9 @@ export class DefaultDeviceProfileTransportConfigurationComponent implements Cont
this.defaultDeviceProfileTransportConfigurationFormGroup = this.fb.group({ this.defaultDeviceProfileTransportConfigurationFormGroup = this.fb.group({
configuration: [null, Validators.required] configuration: [null, Validators.required]
}); });
this.defaultDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { this.defaultDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALIDATORS, NG_VALIDATORS,
@ -29,6 +29,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { DeviceProfileTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; import { DeviceProfileTransportConfiguration, DeviceTransportType } from '@shared/models/device.models';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-device-profile-transport-configuration', selector: 'tb-device-profile-transport-configuration',
@ -64,7 +65,8 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -78,7 +80,9 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu
this.deviceProfileTransportConfigurationFormGroup = this.fb.group({ this.deviceProfileTransportConfigurationFormGroup = this.fb.group({
configuration: [null] configuration: [null]
}); });
this.deviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { this.deviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

5
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts

@ -34,6 +34,7 @@ import { Direction } from '@shared/models/page/sort-order';
import { isDefined, isDefinedAndNotNull, isObject, isString } from '@core/utils'; import { isDefined, isDefinedAndNotNull, isObject, isString } from '@core/utils';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-profile-lwm2m-object-list', selector: 'tb-profile-lwm2m-object-list',
@ -90,7 +91,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
objectsList: [this.objectsList], objectsList: [this.objectsList],
objectLwm2m: [''] objectLwm2m: ['']
}); });
this.lwm2mListFormGroup.get('objectsList').valueChanges.subscribe((value) => { this.lwm2mListFormGroup.get('objectsList').valueChanges.pipe(
takeUntilDestroyed()
).subscribe((value) => {
if (!value.length || (value.length && isObject(value[0]))) { if (!value.length || (value.length && isObject(value[0]))) {
let formValue = null; let formValue = null;
if (this.lwm2mListFormGroup.valid) { if (this.lwm2mListFormGroup.valid) {

10
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, Inject, Input, Optional } from '@angular/core'; import { ChangeDetectorRef, Component, DestroyRef, Inject, Input, Optional } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
@ -24,6 +24,7 @@ import { TranslateService } from '@ngx-translate/core';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { EntityComponent } from '../entity/entity.component'; import { EntityComponent } from '../entity/entity.component';
import { guid } from '@core/utils'; import { guid } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-tenant-profile', selector: 'tb-tenant-profile',
@ -40,7 +41,8 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
@Optional() @Inject('entity') protected entityValue: TenantProfile, @Optional() @Inject('entity') protected entityValue: TenantProfile,
@Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<TenantProfile>, @Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<TenantProfile>,
protected fb: UntypedFormBuilder, protected fb: UntypedFormBuilder,
protected cd: ChangeDetectorRef) { protected cd: ChangeDetectorRef,
private destroyRef: DestroyRef) {
super(store, fb, entityValue, entitiesTableConfigValue, cd); super(store, fb, entityValue, entitiesTableConfigValue, cd);
} }
@ -142,7 +144,9 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
description: [entity ? entity.description : '', []], description: [entity ? entity.description : '', []],
} }
); );
formGroup.get('isolatedTbRuleEngine').valueChanges.subscribe((value) => { formGroup.get('isolatedTbRuleEngine').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((value) => {
if (value) { if (value) {
formGroup.get('profileData').patchValue({ formGroup.get('profileData').patchValue({
queueConfiguration: mainQueue queueConfiguration: mainQueue

4
ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts

@ -128,7 +128,9 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
} }
); );
this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { this.defaultTenantProfileConfigurationFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

3
ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.ts

@ -56,7 +56,6 @@ export class RateLimitsListComponent implements ControlValueAccessor, Validator,
rateLimitsArray: Array<RateLimits>; rateLimitsArray: Array<RateLimits>;
private valueChangeSubscription: Subscription = null;
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
@ -66,7 +65,7 @@ export class RateLimitsListComponent implements ControlValueAccessor, Validator,
this.rateLimitsListFormGroup = this.fb.group({ this.rateLimitsListFormGroup = this.fb.group({
rateLimits: this.fb.array([]) rateLimits: this.fb.array([])
}); });
this.valueChangeSubscription = this.rateLimitsListFormGroup.valueChanges this.rateLimitsListFormGroup.valueChanges
.pipe(takeUntil(this.destroy$)) .pipe(takeUntil(this.destroy$))
.subscribe((value) => { .subscribe((value) => {
this.updateView(value?.rateLimits); this.updateView(value?.rateLimits);

10
ui-ngx/src/app/modules/home/components/profile/tenant/tenant-profile-configuration.component.ts

@ -14,13 +14,14 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { TenantProfileConfiguration, TenantProfileType } from '@shared/models/tenant.model'; import { TenantProfileConfiguration, TenantProfileType } from '@shared/models/tenant.model';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-tenant-profile-configuration', selector: 'tb-tenant-profile-configuration',
@ -55,7 +56,8 @@ export class TenantProfileConfigurationComponent implements ControlValueAccessor
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -69,7 +71,9 @@ export class TenantProfileConfigurationComponent implements ControlValueAccessor
this.tenantProfileConfigurationFormGroup = this.fb.group({ this.tenantProfileConfigurationFormGroup = this.fb.group({
configuration: [null, Validators.required] configuration: [null, Validators.required]
}); });
this.tenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { this.tenantProfileConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

28
ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
UntypedFormBuilder, UntypedFormBuilder,
@ -35,6 +35,7 @@ import {
} from '@shared/models/queue.models'; } from '@shared/models/queue.models';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-queue-form', selector: 'tb-queue-form',
@ -53,7 +54,7 @@ import { Subscription } from 'rxjs';
} }
] ]
}) })
export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator { export class QueueFormComponent implements ControlValueAccessor, OnInit, Validator {
@Input() @Input()
disabled: boolean; disabled: boolean;
@ -77,10 +78,10 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr
private modelValue: QueueInfo; private modelValue: QueueInfo;
private propagateChange = null; private propagateChange = null;
private propagateChangePending = false; private propagateChangePending = false;
private valueChange$: Subscription = null;
constructor(private utils: UtilsService, constructor(private utils: UtilsService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -122,24 +123,23 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr
duplicateMsgToAllPartitions: [false] duplicateMsgToAllPartitions: [false]
}) })
}); });
this.valueChange$ = this.queueFormGroup.valueChanges.subscribe(() => { this.queueFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
this.queueFormGroup.get('name').valueChanges.subscribe((value) => { this.queueFormGroup.get('name').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((value) => {
this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`}); this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`});
}); });
this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => { this.queueFormGroup.get('submitStrategy').get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.submitStrategyTypeChanged(); this.submitStrategyTypeChanged();
}); });
} }
ngOnDestroy() {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
this.valueChange$ = null;
}
}
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled;
if (this.disabled) { if (this.disabled) {

10
ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core'; import { Component, DestroyRef, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -32,6 +32,7 @@ import { forkJoin, Observable } from 'rxjs';
import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component'; import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface RelationDialogData { export interface RelationDialogData {
isAdd: boolean; isAdd: boolean;
@ -65,7 +66,8 @@ export class RelationDialogComponent extends DialogComponent<RelationDialogCompo
private entityRelationService: EntityRelationService, private entityRelationService: EntityRelationService,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<RelationDialogComponent, boolean>, public dialogRef: MatDialogRef<RelationDialogComponent, boolean>,
public fb: UntypedFormBuilder) { public fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.isAdd = data.isAdd; this.isAdd = data.isAdd;
this.direction = data.direction; this.direction = data.direction;
@ -83,7 +85,9 @@ export class RelationDialogComponent extends DialogComponent<RelationDialogCompo
this.relationFormGroup.get('type').disable(); this.relationFormGroup.get('type').disable();
this.relationFormGroup.get('targetEntityIds').disable(); this.relationFormGroup.get('targetEntityIds').disable();
} }
this.additionalInfo.valueChanges.subscribe( this.additionalInfo.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.submitted = false; this.submitted = false;
} }

10
ui-ngx/src/app/modules/home/components/sms/aws-sns-provider-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
@ -25,6 +25,7 @@ import {
SmsProviderConfiguration, SmsProviderConfiguration,
SmsProviderType SmsProviderType
} from '@shared/models/settings.models'; } from '@shared/models/settings.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-aws-sns-provider-configuration', selector: 'tb-aws-sns-provider-configuration',
@ -57,7 +58,8 @@ export class AwsSnsProviderConfigurationComponent implements ControlValueAccesso
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -73,7 +75,9 @@ export class AwsSnsProviderConfigurationComponent implements ControlValueAccesso
secretAccessKey: [null, [Validators.required]], secretAccessKey: [null, [Validators.required]],
region: [null, [Validators.required]] region: [null, [Validators.required]]
}); });
this.awsSnsProviderConfigurationFormGroup.valueChanges.subscribe(() => { this.awsSnsProviderConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { import {
AwsSnsSmsProviderConfiguration, AwsSnsSmsProviderConfiguration,
@ -33,6 +33,7 @@ import {
} from '@shared/models/settings.models'; } from '@shared/models/settings.models';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-smpp-sms-provider-configuration', selector: 'tb-smpp-sms-provider-configuration',
@ -46,7 +47,8 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
}) })
export class SmppSmsProviderConfigurationComponent implements ControlValueAccessor, OnInit{ export class SmppSmsProviderConfigurationComponent implements ControlValueAccessor, OnInit{
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
private requiredValue: boolean; private requiredValue: boolean;
@ -98,7 +100,9 @@ export class SmppSmsProviderConfigurationComponent implements ControlValueAcces
codingScheme: [null, []], codingScheme: [null, []],
}); });
this.smppSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { this.smppSmsProviderConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValue(); this.updateValue();
}); });
} }

14
ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
@ -31,6 +31,7 @@ import {
SmsProviderType, SmsProviderType,
smsProviderTypeTranslationMap smsProviderTypeTranslationMap
} from '@shared/models/settings.models'; } from '@shared/models/settings.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-sms-provider-configuration', selector: 'tb-sms-provider-configuration',
@ -65,7 +66,8 @@ export class SmsProviderConfigurationComponent implements ControlValueAccessor,
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -80,10 +82,14 @@ export class SmsProviderConfigurationComponent implements ControlValueAccessor,
type: [null, Validators.required], type: [null, Validators.required],
configuration: [null, smsProviderConfigurationValidator(true)] configuration: [null, smsProviderConfigurationValidator(true)]
}); });
this.smsProviderConfigurationFormGroup.valueChanges.subscribe(() => { this.smsProviderConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
this.smsProviderConfigurationFormGroup.get('type').valueChanges.subscribe(() => { this.smsProviderConfigurationFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.smsProviderTypeChanged(); this.smsProviderTypeChanged();
}); });
} }

10
ui-ngx/src/app/modules/home/components/sms/twilio-sms-provider-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
@ -26,6 +26,7 @@ import {
SmsProviderType, SmsProviderType,
TwilioSmsProviderConfiguration TwilioSmsProviderConfiguration
} from '@shared/models/settings.models'; } from '@shared/models/settings.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-twilio-sms-provider-configuration', selector: 'tb-twilio-sms-provider-configuration',
@ -60,7 +61,8 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -76,7 +78,9 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce
accountSid: [null, Validators.required], accountSid: [null, Validators.required],
accountToken: [null, Validators.required] accountToken: [null, Validators.required]
}); });
this.twilioSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { this.twilioSmsProviderConfigurationFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

14
ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -40,6 +40,7 @@ import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-entity-types-version-create', selector: 'tb-entity-types-version-create',
@ -80,7 +81,8 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -88,7 +90,9 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
this.entityTypesVersionCreateFormGroup = this.fb.group({ this.entityTypesVersionCreateFormGroup = this.fb.group({
entityTypes: this.fb.array([], []) entityTypes: this.fb.array([], [])
}); });
this.entityTypesVersionCreateFormGroup.valueChanges.subscribe(() => { this.entityTypesVersionCreateFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }
@ -152,7 +156,9 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
} }
); );
this.updateEntityTypeValidators(entityTypeControl); this.updateEntityTypeValidators(entityTypeControl);
entityTypeControl.get('config.allEntities').valueChanges.subscribe(() => { entityTypeControl.get('config.allEntities').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateEntityTypeValidators(entityTypeControl); this.updateEntityTypeValidators(entityTypeControl);
}); });
return entityTypeControl; return entityTypeControl;

10
ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -40,6 +40,7 @@ import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.m
import { MatCheckbox } from '@angular/material/checkbox'; import { MatCheckbox } from '@angular/material/checkbox';
import { TbPopoverService } from '@shared/components/popover.service'; import { TbPopoverService } from '@shared/components/popover.service';
import { RemoveOtherEntitiesConfirmComponent } from '@home/components/vc/remove-other-entities-confirm.component'; import { RemoveOtherEntitiesConfirmComponent } from '@home/components/vc/remove-other-entities-confirm.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-entity-types-version-load', selector: 'tb-entity-types-version-load',
@ -79,7 +80,8 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
private popoverService: TbPopoverService, private popoverService: TbPopoverService,
private renderer: Renderer2, private renderer: Renderer2,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -87,7 +89,9 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
this.entityTypesVersionLoadFormGroup = this.fb.group({ this.entityTypesVersionLoadFormGroup = this.fb.group({
entityTypes: this.fb.array([], []) entityTypes: this.fb.array([], [])
}); });
this.entityTypesVersionLoadFormGroup.valueChanges.subscribe(() => { this.entityTypesVersionLoadFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

14
ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, DestroyRef, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { UntypedFormBuilder, UntypedFormGroup, FormGroupDirective, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, FormGroupDirective, Validators } from '@angular/forms';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
@ -35,6 +35,7 @@ import { catchError, mergeMap, take } from 'rxjs/operators';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-repository-settings', selector: 'tb-repository-settings',
@ -71,7 +72,8 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit
private dialogService: DialogService, private dialogService: DialogService,
private translate: TranslateService, private translate: TranslateService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
public fb: UntypedFormBuilder) { public fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -89,10 +91,14 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit
privateKeyPassword: [null, []] privateKeyPassword: [null, []]
}); });
this.updateValidators(false); this.updateValidators(false);
this.repositorySettingsForm.get('authMethod').valueChanges.subscribe(() => { this.repositorySettingsForm.get('authMethod').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
this.repositorySettingsForm.get('privateKeyFileName').valueChanges.subscribe(() => { this.repositorySettingsForm.get('privateKeyFileName').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(false); this.updateValidators(false);
}); });
this.store.pipe( this.store.pipe(

9
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts

@ -17,6 +17,7 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
EventEmitter, EventEmitter,
forwardRef, forwardRef,
Input, Input,
@ -53,6 +54,7 @@ import {
AggregatedValueCardKeySettings AggregatedValueCardKeySettings
} from '@home/components/widget/lib/cards/aggregated-value-card.models'; } from '@home/components/widget/lib/cards/aggregated-value-card.models';
import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-aggregated-data-key-row', selector: 'tb-aggregated-data-key-row',
@ -128,7 +130,8 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
public translate: TranslateService, public translate: TranslateService,
public truncate: TruncatePipe, public truncate: TruncatePipe,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
@ -140,7 +143,9 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn
color: [null, []], color: [null, []],
showArrow: [null, []] showArrow: [null, []]
}); });
this.keyRowFormGroup.valueChanges.subscribe( this.keyRowFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => this.updateModel() () => this.updateModel()
); );
} }

9
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts

@ -17,6 +17,7 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
forwardRef, forwardRef,
Input, Input,
OnChanges, OnChanges,
@ -39,6 +40,7 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models';
import { aggregatedValueCardDefaultKeySettings } from '@home/components/widget/lib/cards/aggregated-value-card.models'; import { aggregatedValueCardDefaultKeySettings } from '@home/components/widget/lib/cards/aggregated-value-card.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-aggregated-data-keys-panel', selector: 'tb-aggregated-data-keys-panel',
@ -87,14 +89,17 @@ export class AggregatedDataKeysPanelComponent implements ControlValueAccessor, O
private dialog: MatDialog, private dialog: MatDialog,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private utils: UtilsService, private utils: UtilsService,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
this.keysListFormGroup = this.fb.group({ this.keysListFormGroup = this.fb.group({
keys: [this.fb.array([]), []] keys: [this.fb.array([]), []]
}); });
this.keysListFormGroup.valueChanges.subscribe( this.keysListFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
(val) => this.propagateChange(this.keysListFormGroup.get('keys').value) (val) => this.propagateChange(this.keysListFormGroup.get('keys').value)
); );
this.updateParams(); this.updateParams();

14
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { ChangeDetectorRef, Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
@ -29,6 +29,7 @@ import {
DatasourceType DatasourceType
} from '@shared/models/widget.models'; } from '@shared/models/widget.models';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-comparison-key-row', selector: 'tb-comparison-key-row',
@ -60,7 +61,8 @@ export class ComparisonKeyRowComponent implements ControlValueAccessor, OnInit {
private propagateChange = (_val: any) => {}; private propagateChange = (_val: any) => {};
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
@ -70,10 +72,14 @@ export class ComparisonKeyRowComponent implements ControlValueAccessor, OnInit {
comparisonValuesLabel: [null, []], comparisonValuesLabel: [null, []],
color: [null, []] color: [null, []]
}); });
this.keyRowFormGroup.valueChanges.subscribe( this.keyRowFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => this.updateModel() () => this.updateModel()
); );
this.keyRowFormGroup.get('showValuesForComparison').valueChanges.subscribe(() => this.updateValidators()); this.keyRowFormGroup.get('showValuesForComparison').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => this.updateValidators());
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {

10
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-keys-table.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -24,6 +24,7 @@ import {
UntypedFormGroup UntypedFormGroup
} from '@angular/forms'; } from '@angular/forms';
import { DataKey, DatasourceType } from '@shared/models/widget.models'; import { DataKey, DatasourceType } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-comparison-keys-table', selector: 'tb-comparison-keys-table',
@ -55,14 +56,17 @@ export class ComparisonKeysTableComponent implements ControlValueAccessor, OnIni
private propagateChange = (_val: any) => {}; private propagateChange = (_val: any) => {};
constructor(private fb: UntypedFormBuilder) { constructor(private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
this.keysListFormGroup = this.fb.group({ this.keysListFormGroup = this.fb.group({
keys: [this.fb.array([]), []] keys: [this.fb.array([]), []]
}); });
this.keysListFormGroup.valueChanges.subscribe( this.keysListFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
const keys: DataKey[] = this.keysListFormGroup.get('keys').value; const keys: DataKey[] = this.keysListFormGroup.get('keys').value;
this.propagateChange(keys); this.propagateChange(keys);

5
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts

@ -52,6 +52,7 @@ import {
TimeSeriesChartYAxisId TimeSeriesChartYAxisId
} from '@home/components/widget/lib/chart/time-series-chart.models'; } from '@home/components/widget/lib/chart/time-series-chart.models';
import { TimeSeriesChartTooltipTrigger } from '@home/components/widget/lib/chart/time-series-chart-tooltip.models'; import { TimeSeriesChartTooltipTrigger } from '@home/components/widget/lib/chart/time-series-chart-tooltip.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-time-series-chart-basic-config', selector: 'tb-time-series-chart-basic-config',
@ -204,7 +205,9 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
if (this.chartType === TimeSeriesChartType.state) { if (this.chartType === TimeSeriesChartType.state) {
this.timeSeriesChartWidgetConfigForm.addControl('states', this.fb.control(settings.states, [])); this.timeSeriesChartWidgetConfigForm.addControl('states', this.fb.control(settings.states, []));
} }
this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').valueChanges.subscribe(() => this.updateSeriesState()); this.timeSeriesChartWidgetConfigForm.get('comparisonEnabled').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => this.updateSeriesState());
} }
protected prepareOutputConfig(config: any): WidgetConfigComponentData { protected prepareOutputConfig(config: any): WidgetConfigComponentData {

9
ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts

@ -17,6 +17,7 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
EventEmitter, EventEmitter,
forwardRef, forwardRef,
Input, Input,
@ -66,6 +67,7 @@ import {
TimeSeriesChartYAxisId TimeSeriesChartYAxisId
} from '@home/components/widget/lib/chart/time-series-chart.models'; } from '@home/components/widget/lib/chart/time-series-chart.models';
import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const dataKeyValid = (key: DataKey): boolean => !!key && !!key.type && !!key.name; export const dataKeyValid = (key: DataKey): boolean => !!key && !!key.type && !!key.name;
@ -234,7 +236,8 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private dialog: MatDialog, private dialog: MatDialog,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
@ -252,7 +255,9 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan
this.keyRowFormGroup.addControl('yAxis', this.fb.control(null)); this.keyRowFormGroup.addControl('yAxis', this.fb.control(null));
this.keyRowFormGroup.addControl('timeSeriesType', this.fb.control(null)); this.keyRowFormGroup.addControl('timeSeriesType', this.fb.control(null));
} }
merge(this.keyFormControl.valueChanges, this.keyRowFormGroup.valueChanges).subscribe( merge(this.keyFormControl.valueChanges, this.keyRowFormGroup.valueChanges).pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => this.updateModel() () => this.updateModel()
); );
} }

9
ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts

@ -17,6 +17,7 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef,
forwardRef, forwardRef,
Input, Input,
OnChanges, OnChanges,
@ -45,6 +46,7 @@ import { UtilsService } from '@core/services/utils.service';
import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { TimeSeriesChartYAxisId } from '@home/components/widget/lib/chart/time-series-chart.models'; import { TimeSeriesChartYAxisId } from '@home/components/widget/lib/chart/time-series-chart.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-data-keys-panel', selector: 'tb-data-keys-panel',
@ -180,14 +182,17 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC
private dialog: MatDialog, private dialog: MatDialog,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private utils: UtilsService, private utils: UtilsService,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
this.keysListFormGroup = this.fb.group({ this.keysListFormGroup = this.fb.group({
keys: [this.fb.array([]), []] keys: [this.fb.array([]), []]
}); });
this.keysListFormGroup.valueChanges.subscribe( this.keysListFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
let keys: DataKey[] = this.keysListFormGroup.get('keys').value; let keys: DataKey[] = this.keysListFormGroup.get('keys').value;
if (keys) { if (keys) {

10
ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { WidgetActionsData } from '@home/components/widget/action/manage-widget-actions.component.models'; import { WidgetActionsData } from '@home/components/widget/action/manage-widget-actions.component.models';
@ -25,6 +25,7 @@ import {
} from '@home/components/widget/action/manage-widget-actions-dialog.component'; } from '@home/components/widget/action/manage-widget-actions-dialog.component';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-widget-actions-panel', selector: 'tb-widget-actions-panel',
@ -50,14 +51,17 @@ export class WidgetActionsPanelComponent implements ControlValueAccessor, OnInit
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private dialog: MatDialog, private dialog: MatDialog,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
this.actionsFormGroup = this.fb.group({ this.actionsFormGroup = this.fb.group({
actions: [null, []] actions: [null, []]
}); });
this.actionsFormGroup.get('actions').valueChanges.subscribe( this.actionsFormGroup.get('actions').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
(val) => this.propagateChange(val) (val) => this.propagateChange(val)
); );
} }

5
ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts

@ -63,6 +63,7 @@ import { map, share, tap } from 'rxjs/operators';
import { Observable, of, ReplaySubject } from 'rxjs'; import { Observable, of, ReplaySubject } from 'rxjs';
import { ResourcesService } from '@core/services/resources.service'; import { ResourcesService } from '@core/services/resources.service';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'liquid-level-card-basic-config', selector: 'liquid-level-card-basic-config',
@ -228,7 +229,9 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon
actions: [configData.config.actions || {}, []] actions: [configData.config.actions || {}, []]
}); });
this.levelCardWidgetConfigForm.get('selectedShape').valueChanges.subscribe(() => { this.levelCardWidgetConfigForm.get('selectedShape').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.cd.detectChanges(); this.cd.detectChanges();
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges();
}); });

30
ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { Component, DestroyRef, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
@ -58,6 +58,7 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models'
import { WidgetComponentService } from '@home/components/widget/widget-component.service'; import { WidgetComponentService } from '@home/components/widget/widget-component.service';
import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models';
import { isNotEmptyTbFunction, TbFunction } from '@shared/models/js-function.models'; import { isNotEmptyTbFunction, TbFunction } from '@shared/models/js-function.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-data-key-config', selector: 'tb-data-key-config',
@ -188,7 +189,8 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
private translate: TranslateService, private translate: TranslateService,
private widgetService: WidgetService, private widgetService: WidgetService,
private widgetComponentService: WidgetComponentService, private widgetComponentService: WidgetComponentService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
this.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); this.functionScopeVariables = this.widgetService.getWidgetScopeVariables();
} }
@ -240,7 +242,9 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
this.dataKeySettingsFormGroup = this.fb.group({ this.dataKeySettingsFormGroup = this.fb.group({
settings: [null, []] settings: [null, []]
}); });
this.dataKeySettingsFormGroup.valueChanges.subscribe(() => { this.dataKeySettingsFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }
@ -260,7 +264,9 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
postFuncBody: [null, []] postFuncBody: [null, []]
}); });
this.dataKeyFormGroup.get('aggregationType').valueChanges.subscribe( this.dataKeyFormGroup.get('aggregationType').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
(aggType) => { (aggType) => {
if (!this.dataKeyFormGroup.get('label').dirty) { if (!this.dataKeyFormGroup.get('label').dirty) {
let newLabel = this.dataKeyFormGroup.get('name').value; let newLabel = this.dataKeyFormGroup.get('name').value;
@ -274,23 +280,31 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con
} }
); );
this.dataKeyFormGroup.get('comparisonEnabled').valueChanges.subscribe( this.dataKeyFormGroup.get('comparisonEnabled').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.updateComparisonValues(); this.updateComparisonValues();
} }
); );
this.dataKeyFormGroup.get('timeForComparison').valueChanges.subscribe( this.dataKeyFormGroup.get('timeForComparison').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.updateComparisonValues(); this.updateComparisonValues();
} }
); );
this.dataKeyFormGroup.valueChanges.subscribe(() => { this.dataKeyFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
this.dataKeyFormGroup.get('usePostProcessing').valueChanges.subscribe((usePostProcessing: boolean) => { this.dataKeyFormGroup.get('usePostProcessing').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((usePostProcessing: boolean) => {
const postFuncBody: TbFunction = this.dataKeyFormGroup.get('postFuncBody').value; const postFuncBody: TbFunction = this.dataKeyFormGroup.get('postFuncBody').value;
if (usePostProcessing && !isNotEmptyTbFunction(postFuncBody)) { if (usePostProcessing && !isNotEmptyTbFunction(postFuncBody)) {
this.dataKeyFormGroup.get('postFuncBody').patchValue('return value;'); this.dataKeyFormGroup.get('postFuncBody').patchValue('return value;');

14
ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit, Optional } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit, Optional } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALIDATORS, NG_VALIDATORS,
@ -43,6 +43,7 @@ import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/wid
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { DatasourcesComponent } from '@home/components/widget/config/datasources.component'; import { DatasourcesComponent } from '@home/components/widget/config/datasources.component';
import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-datasource', selector: 'tb-datasource',
@ -188,7 +189,8 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
@Optional() @Optional()
private datasourcesComponent: DatasourcesComponent, private datasourcesComponent: DatasourcesComponent,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -238,10 +240,14 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida
if (this.hasAdditionalLatestDataKeys) { if (this.hasAdditionalLatestDataKeys) {
this.datasourceFormGroup.addControl('latestDataKeys', this.fb.control(null)); this.datasourceFormGroup.addControl('latestDataKeys', this.fb.control(null));
} }
this.datasourceFormGroup.get('type').valueChanges.subscribe(() => { this.datasourceFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(); this.updateValidators();
}); });
this.datasourceFormGroup.valueChanges.subscribe( this.datasourceFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.datasourceUpdated(this.datasourceFormGroup.value); this.datasourceUpdated(this.datasourceFormGroup.value);
} }

10
ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -42,6 +42,7 @@ import { UtilsService } from '@core/services/utils.service';
import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-datasources', selector: 'tb-datasources',
@ -149,7 +150,8 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private utils: UtilsService, private utils: UtilsService,
public translate: TranslateService, public translate: TranslateService,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -177,7 +179,9 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid
this.datasourcesFormGroup = this.fb.group({ this.datasourcesFormGroup = this.fb.group({
datasources: this.fb.array([]) datasources: this.fb.array([])
}); });
this.datasourcesFormGroup.valueChanges.subscribe( this.datasourcesFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.datasourcesUpdated(this.datasourcesFormGroup.get('datasources').value); this.datasourcesUpdated(this.datasourcesFormGroup.get('datasources').value);
} }

14
ui-ngx/src/app/modules/home/components/widget/config/target-device.component.ts

@ -24,7 +24,7 @@ import {
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
@ -32,6 +32,7 @@ import { TargetDevice, TargetDeviceType } from '@shared/models/widget.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { EntityAliasSelectCallbacks } from '@home/components/alias/entity-alias-select.component.models'; import { EntityAliasSelectCallbacks } from '@home/components/alias/entity-alias-select.component.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-target-device', selector: 'tb-target-device',
@ -80,7 +81,8 @@ export class TargetDeviceComponent implements ControlValueAccessor, OnInit, Vali
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private utils: UtilsService, private utils: UtilsService,
public translate: TranslateService, public translate: TranslateService,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -111,10 +113,14 @@ export class TargetDeviceComponent implements ControlValueAccessor, OnInit, Vali
deviceId: [null, (!this.widgetEditMode && !this.targetDeviceOptional) ? [Validators.required] : []], deviceId: [null, (!this.widgetEditMode && !this.targetDeviceOptional) ? [Validators.required] : []],
entityAliasId: [null, (!this.widgetEditMode && !this.targetDeviceOptional) ? [Validators.required] : []] entityAliasId: [null, (!this.widgetEditMode && !this.targetDeviceOptional) ? [Validators.required] : []]
}); });
this.targetDeviceFormGroup.get('type').valueChanges.subscribe(() => { this.targetDeviceFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(); this.updateValidators();
}); });
this.targetDeviceFormGroup.valueChanges.subscribe( this.targetDeviceFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.targetDeviceUpdated(this.targetDeviceFormGroup.value); this.targetDeviceUpdated(this.targetDeviceFormGroup.value);
} }

18
ui-ngx/src/app/modules/home/components/widget/config/timewindow-config-panel.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { WidgetConfig, widgetType } from '@shared/models/widget.models'; import { WidgetConfig, widgetType } from '@shared/models/widget.models';
@ -23,6 +23,7 @@ import { TranslateService } from '@ngx-translate/core';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { isDefined } from '@core/utils'; import { isDefined } from '@core/utils';
import { TimewindowStyle } from '@shared/models/widget-settings.models'; import { TimewindowStyle } from '@shared/models/widget-settings.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface TimewindowConfigData { export interface TimewindowConfigData {
useDashboardTimewindow: boolean; useDashboardTimewindow: boolean;
@ -81,7 +82,8 @@ export class TimewindowConfigPanelComponent implements ControlValueAccessor, OnI
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
public translate: TranslateService, public translate: TranslateService,
private widgetConfigComponent: WidgetConfigComponent) { private widgetConfigComponent: WidgetConfigComponent,
private destroyRef: DestroyRef) {
} }
ngOnInit() { ngOnInit() {
@ -91,13 +93,19 @@ export class TimewindowConfigPanelComponent implements ControlValueAccessor, OnI
timewindow: [null, []], timewindow: [null, []],
timewindowStyle: [null, []] timewindowStyle: [null, []]
}); });
this.timewindowConfig.valueChanges.subscribe( this.timewindowConfig.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => this.propagateChange(this.timewindowConfig.getRawValue()) () => this.propagateChange(this.timewindowConfig.getRawValue())
); );
this.timewindowConfig.get('useDashboardTimewindow').valueChanges.subscribe(() => { this.timewindowConfig.get('useDashboardTimewindow').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateTimewindowConfigEnabledState(); this.updateTimewindowConfigEnabledState();
}); });
this.timewindowConfig.get('displayTimewindow').valueChanges.subscribe(() => { this.timewindowConfig.get('displayTimewindow').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateTimewindowConfigEnabledState(); this.updateTimewindowConfigEnabledState();
}); });
} }

14
ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { defaultTimewindowStyle, TimewindowStyle } from '@shared/models/widget-settings.models'; import { defaultTimewindowStyle, TimewindowStyle } from '@shared/models/widget-settings.models';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
@ -23,6 +23,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Timewindow } from '@shared/models/time/time.models'; import { Timewindow } from '@shared/models/time/time.models';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-timewindow-style-panel', selector: 'tb-timewindow-style-panel',
@ -50,7 +51,8 @@ export class TimewindowStylePanelComponent extends PageComponent implements OnIn
previewTimewindowStyle: TimewindowStyle; previewTimewindowStyle: TimewindowStyle;
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
protected store: Store<AppState>) { protected store: Store<AppState>,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -69,13 +71,17 @@ export class TimewindowStylePanelComponent extends PageComponent implements OnIn
); );
this.updatePreviewStyle(this.timewindowStyle); this.updatePreviewStyle(this.timewindowStyle);
this.updateTimewindowStyleEnabledState(); this.updateTimewindowStyleEnabledState();
this.timewindowStyleFormGroup.valueChanges.subscribe((timewindowStyle: TimewindowStyle) => { this.timewindowStyleFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((timewindowStyle: TimewindowStyle) => {
if (this.timewindowStyleFormGroup.valid) { if (this.timewindowStyleFormGroup.valid) {
this.updatePreviewStyle(timewindowStyle); this.updatePreviewStyle(timewindowStyle);
setTimeout(() => {this.popover?.updatePosition();}, 0); setTimeout(() => {this.popover?.updatePosition();}, 0);
} }
}); });
this.timewindowStyleFormGroup.get('showIcon').valueChanges.subscribe(() => { this.timewindowStyleFormGroup.get('showIcon').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateTimewindowStyleEnabledState(); this.updateTimewindowStyleEnabledState();
}); });
} }

13
ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts

@ -18,7 +18,7 @@ import { WidgetActionCallbacks } from '@home/components/widget/action/manage-wid
import { DatasourceCallbacks } from '@home/components/widget/config/datasource.component.models'; import { DatasourceCallbacks } from '@home/components/widget/config/datasource.component.models';
import { WidgetConfigComponentData } from '@home/models/widget-component.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AfterViewInit, Directive, EventEmitter, Inject, OnInit } from '@angular/core'; import { AfterViewInit, DestroyRef, Directive, EventEmitter, inject, Inject, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
@ -27,6 +27,7 @@ import { DataKey, DatasourceType, WidgetConfigMode, widgetType } from '@shared/m
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks; export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks;
@ -77,6 +78,8 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
widgetConfigChangedEmitter = new EventEmitter<WidgetConfigComponentData>(); widgetConfigChangedEmitter = new EventEmitter<WidgetConfigComponentData>();
widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable(); widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable();
protected destroyRef = inject(DestroyRef);
protected constructor(@Inject(Store) protected store: Store<AppState>, protected constructor(@Inject(Store) protected store: Store<AppState>,
protected widgetConfigComponent: WidgetConfigComponent) { protected widgetConfigComponent: WidgetConfigComponent) {
super(store); super(store);
@ -104,11 +107,15 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement
for (const part of path) { for (const part of path) {
control = control.get(part); control = control.get(part);
} }
control.valueChanges.subscribe(() => { control.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true, trigger); this.updateValidators(true, trigger);
}); });
} }
this.configForm().valueChanges.subscribe(() => { this.configForm().valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue())); this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue()));
}); });
} }

4
ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts

@ -154,6 +154,10 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy,
if (this.definedSettingsComponentRef) { if (this.definedSettingsComponentRef) {
this.definedSettingsComponentRef.destroy(); this.definedSettingsComponentRef.destroy();
} }
if (this.changeSubscription) {
this.changeSubscription.unsubscribe();
this.changeSubscription = null;
}
} }
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {

10
ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -31,6 +31,7 @@ import { EntityType } from '@shared/models/entity-type.models';
import { createLabelFromDatasource, isDefinedAndNotNull } from '@core/utils'; import { createLabelFromDatasource, isDefinedAndNotNull } from '@core/utils';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { jsonRequired } from '@shared/components/json-object-edit.component'; import { jsonRequired } from '@shared/components/json-object-edit.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
enum JsonInputWidgetMode { enum JsonInputWidgetMode {
ATTRIBUTE = 'ATTRIBUTE', ATTRIBUTE = 'ATTRIBUTE',
@ -77,7 +78,8 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
private utils: UtilsService, private utils: UtilsService,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private attributeService: AttributeService, private attributeService: AttributeService,
private translate: TranslateService) { private translate: TranslateService,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -137,7 +139,9 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
this.attributeUpdateFormGroup = this.fb.group({ this.attributeUpdateFormGroup = this.fb.group({
currentValue: [{}, validators] currentValue: [{}, validators]
}); });
this.attributeUpdateFormGroup.valueChanges.subscribe(() => { this.attributeUpdateFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.ctx.detectChanges(); this.ctx.detectChanges();
}); });
} }

10
ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-add-dialog.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, OnInit } from '@angular/core'; import { Component, DestroyRef, OnInit } from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
@ -23,6 +23,7 @@ import { MatDialogRef } from '@angular/material/dialog';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { RequestData } from '@shared/models/rpc.models'; import { RequestData } from '@shared/models/rpc.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-persistent-add-dialog', selector: 'tb-persistent-add-dialog',
@ -41,7 +42,8 @@ export class PersistentAddDialogComponent extends DialogComponent<PersistentAddD
protected router: Router, protected router: Router,
public dialogRef: MatDialogRef<PersistentAddDialogComponent, RequestData>, public dialogRef: MatDialogRef<PersistentAddDialogComponent, RequestData>,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private translate: TranslateService) { private translate: TranslateService,
private destroyRef: DestroyRef) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.persistentFormGroup = this.fb.group( this.persistentFormGroup = this.fb.group(
@ -62,7 +64,9 @@ export class PersistentAddDialogComponent extends DialogComponent<PersistentAddD
ngOnInit(): void { ngOnInit(): void {
this.rpcMessageTypeText = this.translate.instant('widgets.persistent-table.message-types.false'); this.rpcMessageTypeText = this.translate.instant('widgets.persistent-table.message-types.false');
this.persistentFormGroup.get('oneWayElseTwoWay').valueChanges.subscribe( this.persistentFormGroup.get('oneWayElseTwoWay').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(
() => { () => {
this.rpcMessageTypeText = this.translate.instant(`widgets.persistent-table.message-types.${this.persistentFormGroup.get('oneWayElseTwoWay').value}`); this.rpcMessageTypeText = this.translate.instant(`widgets.persistent-table.message-types.${this.persistentFormGroup.get('oneWayElseTwoWay').value}`);
} }

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts

@ -14,13 +14,14 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { WidgetFont } from '@home/components/widget/lib/settings/common/widget-font.component'; import { WidgetFont } from '@home/components/widget/lib/settings/common/widget-font.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface LabelWidgetLabel { export interface LabelWidgetLabel {
pattern: string; pattern: string;
@ -61,7 +62,8 @@ export class LabelWidgetLabelComponent extends PageComponent implements OnInit,
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -73,7 +75,9 @@ export class LabelWidgetLabelComponent extends PageComponent implements OnInit,
backgroundColor: [null, []], backgroundColor: [null, []],
font: [null, []] font: [null, []]
}); });
this.labelWidgetLabelFormGroup.valueChanges.subscribe(() => { this.labelWidgetLabelFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

26
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -35,6 +35,7 @@ import { TranslateService } from '@ngx-translate/core';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { IAliasController } from 'src/app/core/api/widget-api.models'; import { IAliasController } from 'src/app/core/api/widget-api.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export function flotDataKeyDefaultSettings(chartType: ChartType): TbFlotKeySettings { export function flotDataKeyDefaultSettings(chartType: ChartType): TbFlotKeySettings {
const settings: TbFlotKeySettings = { const settings: TbFlotKeySettings = {
@ -128,7 +129,8 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private widgetService: WidgetService, private widgetService: WidgetService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -189,23 +191,33 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C
}); });
this.flotKeySettingsFormGroup.get('showLines').valueChanges.subscribe(() => { this.flotKeySettingsFormGroup.get('showLines').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(false); this.updateValidators(false);
}); });
this.flotKeySettingsFormGroup.get('fillLines').valueChanges.subscribe(() => { this.flotKeySettingsFormGroup.get('fillLines').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(false); this.updateValidators(false);
}); });
this.flotKeySettingsFormGroup.get('showPoints').valueChanges.subscribe(() => { this.flotKeySettingsFormGroup.get('showPoints').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(false); this.updateValidators(false);
}); });
this.flotKeySettingsFormGroup.get('comparisonSettings.showValuesForComparison').valueChanges.subscribe(() => { this.flotKeySettingsFormGroup.get('comparisonSettings.showValuesForComparison').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(false); this.updateValidators(false);
}); });
this.flotKeySettingsFormGroup.valueChanges.subscribe(() => { this.flotKeySettingsFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });

19
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts

@ -15,7 +15,16 @@
/// ///
import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component';
import { Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import {
Component,
DestroyRef,
EventEmitter,
forwardRef,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
@ -28,6 +37,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { TbFlotKeyThreshold } from '@home/components/widget/lib/flot-widget.models'; import { TbFlotKeyThreshold } from '@home/components/widget/lib/flot-widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-flot-threshold', selector: 'tb-flot-threshold',
@ -60,7 +70,8 @@ export class FlotThresholdComponent extends PageComponent implements OnInit, Con
public thresholdFormGroup: UntypedFormGroup; public thresholdFormGroup: UntypedFormGroup;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -70,7 +81,9 @@ export class FlotThresholdComponent extends PageComponent implements OnInit, Con
lineWidth: [null, [Validators.min(0)]], lineWidth: [null, [Validators.min(0)]],
color: [null, []] color: [null, []]
}); });
this.thresholdFormGroup.valueChanges.subscribe(() => { this.thresholdFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

38
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -41,6 +41,7 @@ import {
} from '@home/components/widget/lib/settings/chart/label-data-key.component'; } from '@home/components/widget/lib/settings/chart/label-data-key.component';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { defaultLegendConfig, widgetType } from '@shared/models/widget.models'; import { defaultLegendConfig, widgetType } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export const flotDefaultSettings = (chartType: ChartType): Partial<TbFlotSettings> => { export const flotDefaultSettings = (chartType: ChartType): Partial<TbFlotSettings> => {
const settings: Partial<TbFlotSettings> = { const settings: Partial<TbFlotSettings> = {
@ -140,7 +141,8 @@ export class FlotWidgetSettingsComponent extends PageComponent implements OnInit
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private widgetService: WidgetService, private widgetService: WidgetService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -238,34 +240,50 @@ export class FlotWidgetSettingsComponent extends PageComponent implements OnInit
this.flotSettingsFormGroup.addControl('dataKeysListForLabels', this.fb.control(this.fb.array([]), [])); this.flotSettingsFormGroup.addControl('dataKeysListForLabels', this.fb.control(this.fb.array([]), []));
} }
this.flotSettingsFormGroup.get('showTooltip').valueChanges.subscribe(() => { this.flotSettingsFormGroup.get('showTooltip').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
this.flotSettingsFormGroup.get('xaxis.showLabels').valueChanges.subscribe(() => { this.flotSettingsFormGroup.get('xaxis.showLabels').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
this.flotSettingsFormGroup.get('yaxis.showLabels').valueChanges.subscribe(() => { this.flotSettingsFormGroup.get('yaxis.showLabels').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
if (this.chartType === 'graph' || this.chartType === 'bar') { if (this.chartType === 'graph' || this.chartType === 'bar') {
this.flotSettingsFormGroup.get('showLegend').valueChanges.subscribe(() => { this.flotSettingsFormGroup.get('showLegend').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
this.flotSettingsFormGroup.get('comparisonEnabled').valueChanges.subscribe(() => { this.flotSettingsFormGroup.get('comparisonEnabled').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
this.flotSettingsFormGroup.get('timeForComparison').valueChanges.subscribe(() => { this.flotSettingsFormGroup.get('timeForComparison').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
this.flotSettingsFormGroup.get('customLegendEnabled').valueChanges.subscribe(() => { this.flotSettingsFormGroup.get('customLegendEnabled').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(true); this.updateValidators(true);
}); });
} }
this.flotSettingsFormGroup.valueChanges.subscribe(() => { this.flotSettingsFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/label-data-key.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -28,6 +28,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface LabelDataKey { export interface LabelDataKey {
name: string; name: string;
@ -75,7 +76,8 @@ export class LabelDataKeyComponent extends PageComponent implements OnInit, Cont
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -84,7 +86,9 @@ export class LabelDataKeyComponent extends PageComponent implements OnInit, Cont
name: [null, [Validators.required]], name: [null, [Validators.required]],
type: [DataKeyType.attribute, [Validators.required]] type: [DataKeyType.attribute, [Validators.required]]
}); });
this.labelDataKeyFormGroup.valueChanges.subscribe(() => { this.labelDataKeyFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

16
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
@ -41,6 +41,7 @@ import {
chartShapes, chartShapes,
chartShapeTranslations chartShapeTranslations
} from '@home/components/widget/lib/chart/chart.models'; } from '@home/components/widget/lib/chart/chart.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-time-series-chart-line-settings', selector: 'tb-time-series-chart-line-settings',
@ -90,7 +91,8 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private dataKeyConfigComponent: DataKeyConfigComponent, private dataKeyConfigComponent: DataKeyConfigComponent,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -112,14 +114,18 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu
pointSize: [null, [Validators.min(0)]], pointSize: [null, [Validators.min(0)]],
fillAreaSettings: [null, []] fillAreaSettings: [null, []]
}); });
this.lineSettingsFormGroup.valueChanges.subscribe(() => { this.lineSettingsFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
merge(this.lineSettingsFormGroup.get('showLine').valueChanges, merge(this.lineSettingsFormGroup.get('showLine').valueChanges,
this.lineSettingsFormGroup.get('step').valueChanges, this.lineSettingsFormGroup.get('step').valueChanges,
this.lineSettingsFormGroup.get('showPointLabel').valueChanges, this.lineSettingsFormGroup.get('showPointLabel').valueChanges,
this.lineSettingsFormGroup.get('enablePointLabelBackground').valueChanges) this.lineSettingsFormGroup.get('enablePointLabelBackground').valueChanges
.subscribe(() => { ).pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(); this.updateValidators();
}); });
} }

11
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
@ -35,6 +35,7 @@ import { IAliasController } from '@core/api/widget-api.models';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-get-value-action-settings-panel', selector: 'tb-get-value-action-settings-panel',
@ -103,7 +104,8 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private widgetService: WidgetService, private widgetService: WidgetService,
protected store: Store<AppState>) { protected store: Store<AppState>,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -145,7 +147,10 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen
merge(this.getValueSettingsFormGroup.get('action').valueChanges, merge(this.getValueSettingsFormGroup.get('action').valueChanges,
this.getValueSettingsFormGroup.get('dataToValue').get('type').valueChanges, this.getValueSettingsFormGroup.get('dataToValue').get('type').valueChanges,
this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges
).pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(); this.updateValidators();
}); });
this.updateValidators(); this.updateValidators();

18
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
@ -42,6 +42,7 @@ import {
} from '@home/components/widget/lib/settings/common/action/mobile-action-editor.models'; } from '@home/components/widget/lib/settings/common/action/mobile-action-editor.models';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { TbFunction } from '@shared/models/js-function.models'; import { TbFunction } from '@shared/models/js-function.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-mobile-action-editor', selector: 'tb-mobile-action-editor',
@ -81,7 +82,8 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
private propagateChange = (_v: any) => { }; private propagateChange = (_v: any) => { };
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private widgetService: WidgetService) { private widgetService: WidgetService,
private destroyRef: DestroyRef) {
this.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); this.functionScopeVariables = this.widgetService.getWidgetScopeVariables();
} }
@ -98,14 +100,18 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
handleEmptyResultFunction: [null], handleEmptyResultFunction: [null],
handleErrorFunction: [null] handleErrorFunction: [null]
}); });
this.mobileActionFormGroup.get('type').valueChanges.subscribe((type: WidgetMobileActionType) => { this.mobileActionFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((type: WidgetMobileActionType) => {
let action: WidgetMobileActionDescriptor = this.mobileActionFormGroup.value; let action: WidgetMobileActionDescriptor = this.mobileActionFormGroup.value;
if (this.mobileActionTypeFormGroup) { if (this.mobileActionTypeFormGroup) {
action = {...action, ...this.mobileActionTypeFormGroup.value}; action = {...action, ...this.mobileActionTypeFormGroup.value};
} }
this.updateMobileActionType(type, action); this.updateMobileActionType(type, action);
}); });
this.mobileActionFormGroup.valueChanges.subscribe(() => { this.mobileActionFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }
@ -250,7 +256,9 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit
break; break;
} }
} }
this.mobileActionTypeFormGroup.valueChanges.subscribe(() => { this.mobileActionTypeFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

11
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
@ -33,6 +33,7 @@ import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@sh
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { WidgetService } from '@core/http/widget.service'; import { WidgetService } from '@core/http/widget.service';
import { ValueType } from '@shared/models/constants'; import { ValueType } from '@shared/models/constants';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-set-value-action-settings-panel', selector: 'tb-set-value-action-settings-panel',
@ -89,7 +90,8 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen
constructor(private fb: UntypedFormBuilder, constructor(private fb: UntypedFormBuilder,
private widgetService: WidgetService, private widgetService: WidgetService,
protected store: Store<AppState>) { protected store: Store<AppState>,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -122,7 +124,10 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen
merge(this.setValueSettingsFormGroup.get('action').valueChanges, merge(this.setValueSettingsFormGroup.get('action').valueChanges,
this.setValueSettingsFormGroup.get('valueToData').get('type').valueChanges, this.setValueSettingsFormGroup.get('valueToData').get('type').valueChanges,
this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges
).pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(); this.updateValidators();
}); });
this.updateValidators(); this.updateValidators();

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; import { Component, DestroyRef, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
@ -29,6 +29,7 @@ import { IAliasController } from '@core/api/widget-api.models';
import { AdvancedColorRange } from '@shared/models/widget-settings.models'; import { AdvancedColorRange } from '@shared/models/widget-settings.models';
import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models';
import { Datasource } from '@shared/models/widget.models'; import { Datasource } from '@shared/models/widget.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-advanced-range', selector: 'tb-advanced-range',
@ -66,7 +67,8 @@ export class AdvancedRangeComponent extends PageComponent implements OnInit, Con
public advancedRangeLevelFormGroup: UntypedFormGroup; public advancedRangeLevelFormGroup: UntypedFormGroup;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -76,7 +78,9 @@ export class AdvancedRangeComponent extends PageComponent implements OnInit, Con
to: [null, []], to: [null, []],
color: [null, [Validators.required]] color: [null, [Validators.required]]
}); });
this.advancedRangeLevelFormGroup.valueChanges.subscribe(() => { this.advancedRangeLevelFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/auto-date-format-settings-panel.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { import {
AutoDateFormatSettings, defaultAutoDateFormatSettings, AutoDateFormatSettings, defaultAutoDateFormatSettings,
@ -27,6 +27,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-auto-date-format-settings-panel', selector: 'tb-auto-date-format-settings-panel',
@ -59,7 +60,8 @@ export class AutoDateFormatSettingsPanelComponent extends PageComponent implemen
constructor(private date: DatePipe, constructor(private date: DatePipe,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
protected store: Store<AppState>) { protected store: Store<AppState>,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -68,7 +70,9 @@ export class AutoDateFormatSettingsPanelComponent extends PageComponent implemen
for (const unit of formatTimeUnits) { for (const unit of formatTimeUnits) {
this.autoDateFormatFormGroup.addControl(unit, this.autoDateFormatFormGroup.addControl(unit,
this.fb.control(this.autoDateFormatSettings[unit] || this.defaultValues[unit], [Validators.required])); this.fb.control(this.autoDateFormatSettings[unit] || this.defaultValues[unit], [Validators.required]));
this.autoDateFormatFormGroup.get(unit).valueChanges.subscribe((value: string) => { this.autoDateFormatFormGroup.get(unit).valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe((value: string) => {
this.previewText[unit] = this.date.transform(Date.now(), value); this.previewText[unit] = this.date.transform(Date.now(), value);
}); });
this.previewText[unit] = this.date.transform(Date.now(), this.autoDateFormatSettings[unit] || this.defaultValues[unit]); this.previewText[unit] = this.date.transform(Date.now(), this.autoDateFormatSettings[unit] || this.defaultValues[unit]);

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss

@ -17,6 +17,8 @@
.tb-background-settings-panel { .tb-background-settings-panel {
width: 620px; width: 620px;
min-height: 300px;
max-height: 90vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;

27
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts

@ -14,7 +14,16 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import {
ChangeDetectorRef,
Component,
DestroyRef,
EventEmitter,
Input,
OnInit,
Output,
ViewEncapsulation
} from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { import {
backgroundStyle, backgroundStyle,
@ -30,6 +39,7 @@ import { AppState } from '@core/core.state';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ @Component({
selector: 'tb-background-settings-panel', selector: 'tb-background-settings-panel',
@ -64,7 +74,8 @@ export class BackgroundSettingsPanelComponent extends PageComponent implements O
private imagePipe: ImagePipe, private imagePipe: ImagePipe,
private sanitizer: DomSanitizer, private sanitizer: DomSanitizer,
protected store: Store<AppState>, protected store: Store<AppState>,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef,
private destroyRef: DestroyRef) {
super(store); super(store);
} }
@ -81,13 +92,19 @@ export class BackgroundSettingsPanelComponent extends PageComponent implements O
}) })
} }
); );
this.backgroundSettingsFormGroup.get('type').valueChanges.subscribe(() => { this.backgroundSettingsFormGroup.get('type').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
setTimeout(() => {this.popover?.updatePosition();}, 0); setTimeout(() => {this.popover?.updatePosition();}, 0);
}); });
this.backgroundSettingsFormGroup.get('overlay').get('enabled').valueChanges.subscribe(() => { this.backgroundSettingsFormGroup.get('overlay').get('enabled').valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateValidators(); this.updateValidators();
}); });
this.backgroundSettingsFormGroup.valueChanges.subscribe(() => { this.backgroundSettingsFormGroup.valueChanges.pipe(
takeUntilDestroyed(this.destroyRef)
).subscribe(() => {
this.updateBackgroundStyle(); this.updateBackgroundStyle();
}); });
this.updateValidators(); this.updateValidators();

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts

@ -110,7 +110,7 @@ export class BackgroundSettingsComponent implements OnInit, ControlValueAccessor
backgroundSettings: this.modelValue backgroundSettings: this.modelValue
}; };
const backgroundSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, const backgroundSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, BackgroundSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, this.viewContainerRef, BackgroundSettingsPanelComponent, ['left'], true, null,
ctx, ctx,
{}, {},
{}, {}, true); {}, {}, true);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save