This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new e30aa2b845 chore: Load dashboard in single request (#3715)
e30aa2b845 is described below

commit e30aa2b845bab08ab5dbda1d0c7ae4a486514fd4
Author: Dominik Riemer <[email protected]>
AuthorDate: Thu Aug 7 08:50:20 2025 +0200

    chore: Load dashboard in single request (#3715)
    
    * chore: Add API to load dashboard resources in a single request
    
    * Improve widget loading in dashboard
---
 .../model/dashboard/CompositeDashboardModel.java   |  15 +--
 .../model/datalake/DataExplorerWidgetModel.java    |  20 ----
 .../manager/setup/CouchDbInstallationStep.java     |   6 ++
 .../setup/tasks/AddDataLakeMeasureViewTask.java    |  49 +++++++++
 .../management/DataExplorerResourceManager.java    |  31 ++++++
 .../impl/dashboard/DataLakeDashboardResource.java  |  11 +-
 .../core/migrations/AvailableMigrations.java       |   4 +-
 .../v0980/AddDataLakeMeasureViewMigration.java     |  61 +++++++++++
 .../storage/api/IDataLakeMeasureStorage.java       |  12 +--
 .../streampipes/storage/api/INoSqlStorage.java     |   3 +-
 .../storage/couchdb/CouchDbStorageManager.java     |   8 +-
 .../couchdb/impl/DataLakeMeasureStorage.java       |  60 +++++++++++
 .../streampipes/storage/couchdb/utils/Utils.java   |   1 +
 .../src/lib/apis/dashboard.service.ts              |  16 ++-
 .../src/lib/model/dashboard/dashboard.model.ts     |  13 ++-
 .../chart-view/abstract-chart-view.directive.ts    |  47 +++-----
 .../grid-view/dashboard-grid-view.component.ts     |  16 +--
 .../slide-view/dashboard-slide-view.component.ts   |  25 ++---
 .../panel/dashboard-panel.component.html           |  14 ++-
 .../components/panel/dashboard-panel.component.ts  | 120 +++++++++++++--------
 .../data-explorer-chart-container.component.ts     |   4 +-
 .../base/base-data-explorer-widget.directive.ts    |   7 +-
 .../charts/image/image-widget.component.ts         |   8 +-
 .../models/dataview-dashboard.model.ts             |   4 +-
 24 files changed, 384 insertions(+), 171 deletions(-)

diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
similarity index 65%
copy from 
streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
copy to 
streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
index 38cca4a759..cb06527e42 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
@@ -15,14 +15,15 @@
  * limitations under the License.
  *
  */
-package org.apache.streampipes.resource.management;
 
-import org.apache.streampipes.model.dashboard.DashboardModel;
-import org.apache.streampipes.storage.management.StorageDispatcher;
+package org.apache.streampipes.model.dashboard;
 
-public class DataExplorerResourceManager extends 
AbstractCRUDResourceManager<DashboardModel> {
+import org.apache.streampipes.model.datalake.DataExplorerWidgetModel;
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
 
-  public DataExplorerResourceManager() {
-    
super(StorageDispatcher.INSTANCE.getNoSqlStore().getDataExplorerDashboardStorage(),
 DashboardModel.class);
-  }
+import java.util.List;
+
+public record CompositeDashboardModel(DashboardModel dashboard,
+                                      List<DataExplorerWidgetModel> widgets,
+                                      List<DataLakeMeasure> dataLakeMeasures) {
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java
index 8e302ae163..af5d2739c9 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/DataExplorerWidgetModel.java
@@ -45,10 +45,6 @@ public class DataExplorerWidgetModel extends DashboardEntity 
{
   @JsonSerialize(using = CustomMapSerializer.class, as = Map.class)
   private Map<String, Object> timeSettings;
 
-  private String pipelineId;
-  private String measureName;
-
-
   public DataExplorerWidgetModel() {
     super();
     this.baseAppearanceConfig = new HashMap<>();
@@ -97,22 +93,6 @@ public class DataExplorerWidgetModel extends DashboardEntity 
{
     this.dataConfig = dataConfig;
   }
 
-  public String getPipelineId() {
-    return pipelineId;
-  }
-
-  public void setPipelineId(String pipelineId) {
-    this.pipelineId = pipelineId;
-  }
-
-  public String getMeasureName() {
-    return measureName;
-  }
-
-  public void setMeasureName(String measureName) {
-    this.measureName = measureName;
-  }
-
   public Map<String, Object> getTimeSettings() {
     return this.timeSettings;
   }
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
index e1e305e4a9..1e627973c6 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
@@ -19,6 +19,7 @@
 package org.apache.streampipes.manager.setup;
 
 import org.apache.streampipes.manager.setup.design.UserDesignDocument;
+import org.apache.streampipes.manager.setup.tasks.AddDataLakeMeasureViewTask;
 import 
org.apache.streampipes.manager.setup.tasks.AddDefaultPipelineTemplatesTask;
 import org.apache.streampipes.manager.setup.tasks.CreateAssetLinkTypeTask;
 import org.apache.streampipes.manager.setup.tasks.CreateDefaultAssetTask;
@@ -77,6 +78,7 @@ public class CouchDbInstallationStep extends InstallationStep 
{
     addUserView();
     addNotificationView();
     addPipelineView();
+    addDataLakeMeasureView();
   }
 
   private void addNotificationView() {
@@ -168,4 +170,8 @@ public class CouchDbInstallationStep extends 
InstallationStep {
       logFailure(PREPARING_USERS_TEXT, e);
     }
   }
+
+  private void addDataLakeMeasureView() {
+    new AddDataLakeMeasureViewTask().execute();
+  }
 }
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/AddDataLakeMeasureViewTask.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/AddDataLakeMeasureViewTask.java
new file mode 100644
index 0000000000..4ce055c6e7
--- /dev/null
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/tasks/AddDataLakeMeasureViewTask.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.manager.setup.tasks;
+
+import org.apache.streampipes.storage.couchdb.utils.Utils;
+
+import org.lightcouch.DesignDocument;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static 
org.apache.streampipes.manager.setup.design.DesignDocumentUtils.prepareDocument;
+
+public class AddDataLakeMeasureViewTask implements InstallationTask {
+
+  public static final String MEASUREMENT_BY_NAME_VIEW = "_design/measurement";
+
+  public static final String VIEW_NAME = "by-measure-name";
+  public static final String MAP_FUNCTION = "function (doc) { if 
(doc.measureName) { emit(doc.measureName, doc); } }";
+
+  @Override
+  public void execute() {
+    DesignDocument dataLakeDoc = prepareDocument(MEASUREMENT_BY_NAME_VIEW);
+    Map<String, DesignDocument.MapReduce> dataLakeMeasureViews = new 
HashMap<>();
+
+    DesignDocument.MapReduce byNameFn = new DesignDocument.MapReduce();
+    byNameFn.setMap(MAP_FUNCTION);
+
+    dataLakeMeasureViews.put(VIEW_NAME, byNameFn);
+    dataLakeDoc.setViews(dataLakeMeasureViews);
+    
Utils.getCouchDbGsonClient(Utils.DATA_LAKE_DB_NAME).design().synchronizeWithDb(dataLakeDoc);
+  }
+}
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
index 38cca4a759..06edb30d5c 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
@@ -17,12 +17,43 @@
  */
 package org.apache.streampipes.resource.management;
 
+import org.apache.streampipes.model.dashboard.CompositeDashboardModel;
 import org.apache.streampipes.model.dashboard.DashboardModel;
+import org.apache.streampipes.model.datalake.DataExplorerWidgetModel;
+import org.apache.streampipes.storage.api.CRUDStorage;
+import org.apache.streampipes.storage.api.IDataLakeMeasureStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
+import java.util.List;
+import java.util.Map;
+
 public class DataExplorerResourceManager extends 
AbstractCRUDResourceManager<DashboardModel> {
 
+  private final CRUDStorage<DataExplorerWidgetModel> widgetStorage;
+  private final IDataLakeMeasureStorage dataLakeMeasureStorage;
+
   public DataExplorerResourceManager() {
     
super(StorageDispatcher.INSTANCE.getNoSqlStore().getDataExplorerDashboardStorage(),
 DashboardModel.class);
+    this.widgetStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getDataExplorerWidgetStorage();
+    this.dataLakeMeasureStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getDataLakeStorage();
+  }
+
+  public CompositeDashboardModel getCompositeDashboard(String dashboardId) {
+    var dashboard = db.getElementById(dashboardId);
+    var widgets = dashboard.getWidgets().stream().map(w -> 
widgetStorage.getElementById(w.getId())).toList();
+    var dataLakeMeasures = 
getMeasureNames(widgets).stream().map(dataLakeMeasureStorage::getByMeasureName).toList();
+
+    return new CompositeDashboardModel(dashboard, widgets, dataLakeMeasures);
+  }
+
+  private List<String> getMeasureNames(List<DataExplorerWidgetModel> widgets) {
+    return widgets.stream().map(DataExplorerWidgetModel::getDataConfig)
+        .map(dataConfig -> (List<?>) ((Map<?, ?>) 
dataConfig).get("sourceConfigs"))
+        .filter(Map.class::isInstance)
+        .map(Map.class::cast)
+        .map(cfg -> cfg.get("measureName"))
+        .filter(String.class::isInstance)
+        .map(String.class::cast)
+        .toList();
   }
 }
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
index 75eaded908..f97b7083a1 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
@@ -20,8 +20,9 @@ package org.apache.streampipes.rest.impl.dashboard;
 
 
 import org.apache.streampipes.model.client.user.DefaultPrivilege;
+import org.apache.streampipes.model.dashboard.CompositeDashboardModel;
 import org.apache.streampipes.model.dashboard.DashboardModel;
-import org.apache.streampipes.resource.management.AbstractCRUDResourceManager;
+import org.apache.streampipes.resource.management.DataExplorerResourceManager;
 import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 
 import org.springframework.http.MediaType;
@@ -56,6 +57,12 @@ public class DataLakeDashboardResource extends 
AbstractAuthGuardedRestResource {
     return getResourceManager().find(dashboardId);
   }
 
+  @GetMapping(path = "/{dashboardId}/composite", produces = 
MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasReadAuthority() and hasPermission(#dashboardId, 
'READ')")
+  public CompositeDashboardModel 
getCompositeDashboardModel(@PathVariable("dashboardId") String dashboardId) {
+    return getResourceManager().getCompositeDashboard(dashboardId);
+  }
+
   @PutMapping(path = "/{dashboardId}", produces = 
MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize("this.hasWriteAuthority() and 
hasPermission(#dashboardModel.couchDbId, 'WRITE')")
   public ResponseEntity<DashboardModel> modifyDashboard(@RequestBody 
DashboardModel dashboardModel) {
@@ -77,7 +84,7 @@ public class DataLakeDashboardResource extends 
AbstractAuthGuardedRestResource {
     return ok();
   }
 
-  private AbstractCRUDResourceManager<DashboardModel> getResourceManager() {
+  private DataExplorerResourceManager getResourceManager() {
     return getSpResourceManager().manageDataExplorer();
   }
 
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
index e5e79b786c..b547059f59 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
@@ -26,6 +26,7 @@ import 
org.apache.streampipes.service.core.migrations.v090.UpdateUsernameViewMig
 import org.apache.streampipes.service.core.migrations.v093.AdapterMigration;
 import 
org.apache.streampipes.service.core.migrations.v093.StoreEmailTemplatesMigration;
 import 
org.apache.streampipes.service.core.migrations.v095.MergeFilenamesAndRenameDuplicatesMigration;
+import 
org.apache.streampipes.service.core.migrations.v0980.AddDataLakeMeasureViewMigration;
 import 
org.apache.streampipes.service.core.migrations.v0980.ModifyAssetLinkTypesMigration;
 import 
org.apache.streampipes.service.core.migrations.v0980.ModifyAssetLinksMigration;
 import 
org.apache.streampipes.service.core.migrations.v970.AddDataLakePipelineTemplateMigration;
@@ -56,7 +57,8 @@ public class AvailableMigrations {
         new AddRolesToUserDbMigration(),
         new AddDataLakePipelineTemplateMigration(),
         new ModifyAssetLinksMigration(),
-        new ModifyAssetLinkTypesMigration()
+        new ModifyAssetLinkTypesMigration(),
+        new AddDataLakeMeasureViewMigration()
     );
   }
 }
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v0980/AddDataLakeMeasureViewMigration.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v0980/AddDataLakeMeasureViewMigration.java
new file mode 100644
index 0000000000..640f132c69
--- /dev/null
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v0980/AddDataLakeMeasureViewMigration.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.service.core.migrations.v0980;
+
+import org.apache.streampipes.manager.setup.tasks.AddDataLakeMeasureViewTask;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.couchdb.utils.Utils;
+
+import org.lightcouch.NoDocumentException;
+
+import java.io.IOException;
+
+public class AddDataLakeMeasureViewMigration implements Migration {
+
+  @Override
+  public boolean shouldExecute() {
+    try {
+      var designDoc = Utils
+          .getCouchDbGsonClient(Utils.DATA_LAKE_DB_NAME)
+          .design()
+          .getFromDb(AddDataLakeMeasureViewTask.MEASUREMENT_BY_NAME_VIEW);
+      var viewKey = AddDataLakeMeasureViewTask.VIEW_NAME;
+      var viewMapFunction = AddDataLakeMeasureViewTask.MAP_FUNCTION;
+      var views = designDoc.getViews();
+
+      if (views.containsKey(viewKey)) {
+        return !(views.get(viewKey).getMap().equals(viewMapFunction));
+      } else {
+        return true;
+      }
+    } catch (NoDocumentException e) {
+      return true;
+    }
+  }
+
+  @Override
+  public void executeMigration() throws IOException {
+    new AddDataLakeMeasureViewTask().execute();
+  }
+
+  @Override
+  public String getDescription() {
+    return "Creating view for data lake measure database";
+  }
+}
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IDataLakeMeasureStorage.java
similarity index 65%
copy from 
streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
copy to 
streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IDataLakeMeasureStorage.java
index 38cca4a759..cab2575714 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
+++ 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IDataLakeMeasureStorage.java
@@ -15,14 +15,12 @@
  * limitations under the License.
  *
  */
-package org.apache.streampipes.resource.management;
 
-import org.apache.streampipes.model.dashboard.DashboardModel;
-import org.apache.streampipes.storage.management.StorageDispatcher;
+package org.apache.streampipes.storage.api;
 
-public class DataExplorerResourceManager extends 
AbstractCRUDResourceManager<DashboardModel> {
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
 
-  public DataExplorerResourceManager() {
-    
super(StorageDispatcher.INSTANCE.getNoSqlStore().getDataExplorerDashboardStorage(),
 DashboardModel.class);
-  }
+public interface IDataLakeMeasureStorage extends CRUDStorage<DataLakeMeasure> {
+
+  DataLakeMeasure getByMeasureName(String measureName);
 }
diff --git 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
index 742adf1409..ec330a4a7e 100644
--- 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
+++ 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/INoSqlStorage.java
@@ -24,7 +24,6 @@ import org.apache.streampipes.model.client.user.Role;
 import org.apache.streampipes.model.client.user.UserActivationToken;
 import org.apache.streampipes.model.dashboard.DashboardModel;
 import org.apache.streampipes.model.datalake.DataExplorerWidgetModel;
-import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import 
org.apache.streampipes.model.extensions.configuration.SpServiceConfiguration;
 import 
org.apache.streampipes.model.extensions.svcdiscovery.SpServiceRegistration;
 import org.apache.streampipes.model.file.FileMetadata;
@@ -48,7 +47,7 @@ public interface INoSqlStorage {
 
   INotificationStorage getNotificationStorageApi();
 
-  CRUDStorage<DataLakeMeasure> getDataLakeStorage();
+  IDataLakeMeasureStorage getDataLakeStorage();
 
   CRUDStorage<FileMetadata> getFileMetadataStorage();
 
diff --git 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
index c6edb2cfcf..ea54cf6e0a 100644
--- 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
+++ 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/CouchDbStorageManager.java
@@ -31,6 +31,7 @@ import org.apache.streampipes.model.file.FileMetadata;
 import org.apache.streampipes.model.template.CompactPipelineTemplate;
 import org.apache.streampipes.storage.api.CRUDStorage;
 import org.apache.streampipes.storage.api.IAdapterStorage;
+import org.apache.streampipes.storage.api.IDataLakeMeasureStorage;
 import org.apache.streampipes.storage.api.IDataProcessorStorage;
 import org.apache.streampipes.storage.api.IDataSinkStorage;
 import org.apache.streampipes.storage.api.IDataStreamStorage;
@@ -48,6 +49,7 @@ import org.apache.streampipes.storage.api.IUserStorage;
 import 
org.apache.streampipes.storage.couchdb.impl.AdapterDescriptionStorageImpl;
 import org.apache.streampipes.storage.couchdb.impl.AdapterInstanceStorageImpl;
 import 
org.apache.streampipes.storage.couchdb.impl.CoreConfigurationStorageImpl;
+import org.apache.streampipes.storage.couchdb.impl.DataLakeMeasureStorage;
 import org.apache.streampipes.storage.couchdb.impl.DataProcessorStorageImpl;
 import org.apache.streampipes.storage.couchdb.impl.DataSinkStorageImpl;
 import org.apache.streampipes.storage.couchdb.impl.DataStreamStorageImpl;
@@ -115,9 +117,9 @@ public enum CouchDbStorageManager implements INoSqlStorage {
   }
 
   @Override
-  public CRUDStorage<DataLakeMeasure> getDataLakeStorage() {
-    return new DefaultCrudStorage<>(
-        () -> Utils.getCouchDbGsonClient("data-lake"),
+  public IDataLakeMeasureStorage getDataLakeStorage() {
+    return new DataLakeMeasureStorage(
+        () -> Utils.getCouchDbGsonClient(Utils.DATA_LAKE_DB_NAME),
         DataLakeMeasure.class
     );
   }
diff --git 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/DataLakeMeasureStorage.java
 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/DataLakeMeasureStorage.java
new file mode 100644
index 0000000000..38dec9498b
--- /dev/null
+++ 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/DataLakeMeasureStorage.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.storage.couchdb.impl;
+
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
+import org.apache.streampipes.storage.api.IDataLakeMeasureStorage;
+
+import org.lightcouch.CouchDbClient;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public class DataLakeMeasureStorage extends 
DefaultCrudStorage<DataLakeMeasure> implements IDataLakeMeasureStorage {
+
+  public static final String MEASUREMENT_BY_NAME_VIEW = 
"measurement/by-measure-name";
+
+  public DataLakeMeasureStorage(Supplier<CouchDbClient> couchDbClientSupplier, 
Class<DataLakeMeasure> clazz) {
+    super(couchDbClientSupplier, clazz);
+  }
+
+  @Override
+  public DataLakeMeasure getByMeasureName(String measureName) {
+    List<DataLakeMeasure> results = couchDbClientSupplier.get()
+        .view(MEASUREMENT_BY_NAME_VIEW)
+        .key(measureName)
+        .includeDocs(true)
+        .limit(1)
+        .query(DataLakeMeasure.class);
+
+    if (!results.isEmpty()) {
+      return results.get(0);
+    } else {
+      return null;
+    }
+  }
+
+  @Override
+  public List<DataLakeMeasure> findAll() {
+    return couchDbClientSupplier.get()
+        .view(MEASUREMENT_BY_NAME_VIEW)
+        .includeDocs(true)
+        .query(DataLakeMeasure.class);
+  }
+}
diff --git 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Utils.java
 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Utils.java
index b6f9bf20ba..b98d8bafef 100644
--- 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Utils.java
+++ 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/utils/Utils.java
@@ -33,6 +33,7 @@ import org.lightcouch.CouchDbProperties;
 public class Utils {
 
   public static final String USER_DB_NAME = "users";
+  public static final String DATA_LAKE_DB_NAME = "data-lake";
 
   public static CouchDbClient getCouchDbDataProcessorDescriptionClient() {
     return getCouchDbGsonClient("data-processor");
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts 
b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
index 5e7753e257..4a40b03218 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
@@ -19,9 +19,11 @@
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
 import { SharedDatalakeRestService } from './shared-dashboard.service';
-import { Dashboard } from '../model/dashboard/dashboard.model';
+import {
+    CompositeDashboard,
+    Dashboard,
+} from '../model/dashboard/dashboard.model';
 
 @Injectable({
     providedIn: 'root',
@@ -37,9 +39,13 @@ export class DashboardService {
     }
 
     getDashboard(dashboardId: string): Observable<Dashboard> {
-        return this.http
-            .get(`${this.dashboardUrl}/${dashboardId}`)
-            .pipe(map(data => data as Dashboard));
+        return this.http.get<Dashboard>(`${this.dashboardUrl}/${dashboardId}`);
+    }
+
+    getCompositeDashboard(dashboardId: string): Observable<CompositeDashboard> 
{
+        return this.http.get<CompositeDashboard>(
+            `${this.dashboardUrl}/${dashboardId}/composite`,
+        );
     }
 
     updateDashboard(dashboard: Dashboard): Observable<Dashboard> {
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
index 134490d0ce..927d1a1946 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
@@ -18,7 +18,12 @@
 
 import { GridsterConfig, GridsterItem } from 'angular-gridster2';
 import { TimeSettings } from '../datalake/DateRange';
-import { ResourceMetadata } from '../gen/streampipes-model';
+import {
+    DashboardModel,
+    DataExplorerWidgetModel,
+    DataLakeMeasure,
+    ResourceMetadata,
+} from '../gen/streampipes-model';
 
 // tslint:disable-next-line:no-empty-interface
 export interface DashboardConfig extends GridsterConfig {}
@@ -49,3 +54,9 @@ export interface Dashboard {
     metadata: ResourceMetadata;
     rev?: string;
 }
+
+export interface CompositeDashboard {
+    dashboard: Dashboard;
+    dataLakeMeasures: DataLakeMeasure[];
+    widgets: DataExplorerWidgetModel[];
+}
diff --git 
a/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts 
b/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
index f75ab2f283..bf3adc6ad4 100644
--- 
a/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
+++ 
b/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
@@ -16,7 +16,7 @@
  *
  */
 
-import { Directive, EventEmitter, Input, Output } from '@angular/core';
+import { Directive, EventEmitter, inject, Input, Output } from '@angular/core';
 import {
     ChartService,
     Dashboard,
@@ -25,13 +25,19 @@ import {
     TimeSettings,
 } from '@streampipes/platform-services';
 import { ResizeService } from 
'../../../data-explorer-shared/services/resize.service';
-import { of, zip } from 'rxjs';
 import { DataExplorerChartRegistry } from 
'../../../data-explorer-shared/registry/data-explorer-chart-registry';
-import { catchError } from 'rxjs/operators';
 
 @Directive()
 export abstract class AbstractChartViewDirective {
-    _dashboard: Dashboard;
+    protected resizeService = inject(ResizeService);
+    protected dataViewDataExplorerService = inject(ChartService);
+    protected widgetRegistryService = inject(DataExplorerChartRegistry);
+
+    @Input()
+    dashboard: Dashboard;
+
+    @Input()
+    widgets: DataExplorerWidgetModel[] = [];
 
     @Input()
     editMode: boolean;
@@ -61,39 +67,20 @@ export abstract class AbstractChartViewDirective {
     @Output() startEditModeEmitter: EventEmitter<DataExplorerWidgetModel> =
         new EventEmitter<DataExplorerWidgetModel>();
 
-    constructor(
-        protected resizeService: ResizeService,
-        protected dataViewDataExplorerService: ChartService,
-        protected widgetRegistryService: DataExplorerChartRegistry,
-    ) {}
-
     startEditMode(value: DataExplorerWidgetModel) {
         this.startEditModeEmitter.emit(value);
         this.currentlyConfiguredWidgetId = value.elementId;
     }
 
-    @Input() set dashboard(dashboard: Dashboard) {
-        this._dashboard = dashboard;
-        this.loadWidgetConfigs();
-    }
-
-    get dashboard() {
-        return this._dashboard;
-    }
-
     loadWidgetConfigs() {
-        const observables = this.dashboard.widgets.map(w =>
-            this.dataViewDataExplorerService
-                .getChart(w.id)
-                .pipe(catchError(() => of(undefined))),
-        );
-        zip(...observables).subscribe(results => {
-            results.forEach(r => {
-                this.processWidget(r);
-                this.onWidgetsAvailable();
-                this.widgetsAvailable = true;
-            });
+        this.dashboard.widgets.forEach(widgetConfig => {
+            const availableWidget = this.widgets.find(
+                w => w.elementId === widgetConfig.id,
+            );
+            this.processWidget(availableWidget);
         });
+        this.onWidgetsAvailable();
+        this.widgetsAvailable = true;
     }
 
     loadWidgetConfig(widgetId: string, setCurrentlyConfigured?: boolean) {
diff --git 
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
 
b/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
index 43f8a6a37c..481454a769 100644
--- 
a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
+++ 
b/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
@@ -27,10 +27,7 @@ import {
 import { GridsterItemComponent, GridType } from 'angular-gridster2';
 import { GridsterInfo } from 
'../../../../data-explorer-shared/models/gridster-info.model';
 import { IDataViewDashboardConfig } from 
'../../../../data-explorer-shared/models/dataview-dashboard.model';
-import { ResizeService } from 
'../../../../data-explorer-shared/services/resize.service';
-import { ChartService } from '@streampipes/platform-services';
 import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
-import { DataExplorerChartRegistry } from 
'../../../../data-explorer-shared/registry/data-explorer-chart-registry';
 
 @Component({
     selector: 'sp-dashboard-grid-view',
@@ -48,19 +45,8 @@ export class DashboardGridViewComponent
     @ViewChildren(GridsterItemComponent)
     gridsterItemComponents: QueryList<GridsterItemComponent>;
 
-    constructor(
-        protected resizeService: ResizeService,
-        protected dataViewDataExplorerService: ChartService,
-        protected widgetRegistryService: DataExplorerChartRegistry,
-    ) {
-        super(
-            resizeService,
-            dataViewDataExplorerService,
-            widgetRegistryService,
-        );
-    }
-
     ngOnInit(): void {
+        this.loadWidgetConfigs();
         this.options = {
             disablePushOnDrag: true,
             draggable: { enabled: this.editMode },
diff --git 
a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
 
b/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
index d5169600c6..7d6dd7b283 100644
--- 
a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
+++ 
b/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
@@ -16,16 +16,19 @@
  *
  */
 
-import { AfterViewInit, Component, ElementRef, ViewChild } from 
'@angular/core';
+import {
+    AfterViewInit,
+    Component,
+    ElementRef,
+    OnInit,
+    ViewChild,
+} from '@angular/core';
 import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
-import { ResizeService } from 
'../../../../data-explorer-shared/services/resize.service';
 import {
-    ChartService,
     DashboardItem,
     DataExplorerWidgetModel,
     DataLakeMeasure,
 } from '@streampipes/platform-services';
-import { DataExplorerChartRegistry } from 
'../../../../data-explorer-shared/registry/data-explorer-chart-registry';
 
 @Component({
     selector: 'sp-dashboard-slide-view',
@@ -35,7 +38,7 @@ import { DataExplorerChartRegistry } from 
'../../../../data-explorer-shared/regi
 })
 export class DashboardSlideViewComponent
     extends AbstractChartViewDirective
-    implements AfterViewInit
+    implements OnInit, AfterViewInit
 {
     selectedWidgetIndex = 0;
 
@@ -49,16 +52,8 @@ export class DashboardSlideViewComponent
 
     @ViewChild('slideViewOuter') slideViewOuter: ElementRef;
 
-    constructor(
-        protected resizeService: ResizeService,
-        protected dataViewDataExplorerService: ChartService,
-        protected widgetRegistryService: DataExplorerChartRegistry,
-    ) {
-        super(
-            resizeService,
-            dataViewDataExplorerService,
-            widgetRegistryService,
-        );
+    ngOnInit() {
+        this.loadWidgetConfigs();
     }
 
     selectWidget(index: number, widgetId: string): void {
diff --git 
a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html 
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
index ad1f0e154e..d2627f69c7 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
@@ -50,11 +50,13 @@
                 class="designer-panel"
             >
                 <div fxLayout="column" fxFlex="100">
-                    <sp-dashboard-chart-selection-panel
-                        (addChartEmitter)="addChartToDashboard($event)"
-                        fxFlex="100"
-                    >
-                    </sp-dashboard-chart-selection-panel>
+                    @if (editMode) {
+                        <sp-dashboard-chart-selection-panel
+                            (addChartEmitter)="addChartToDashboard($event)"
+                            fxFlex="100"
+                        >
+                        </sp-dashboard-chart-selection-panel>
+                    }
                 </div>
             </mat-drawer>
             <mat-drawer-content class="h-100 dashboard-grid">
@@ -77,6 +79,7 @@
                     *ngIf="dashboard.widgets.length > 0 && viewMode === 'grid'"
                     [editMode]="editMode"
                     [dashboard]="dashboard"
+                    [widgets]="widgets"
                     [timeSettings]="timeSettings"
                     (deleteCallback)="removeChartFromDashboard($event)"
                     (startEditModeEmitter)="startEditMode($event)"
@@ -88,6 +91,7 @@
                     #dashboardSlide
                     [editMode]="editMode"
                     [dashboard]="dashboard"
+                    [widgets]="widgets"
                     [timeSettings]="timeSettings"
                     (deleteCallback)="removeChartFromDashboard($event)"
                     (startEditModeEmitter)="startEditMode($event)"
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts 
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
index 5245ab3706..4931bc6417 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -63,6 +63,7 @@ export class DashboardPanelComponent
     dashboardLoaded = false;
     originalDashboard: Dashboard;
     dashboard: Dashboard;
+    widgets: DataExplorerWidgetModel[] = [];
 
     /**
      * This is the date range (start, end) to view the data and is set in 
data-explorer.ts
@@ -73,11 +74,8 @@ export class DashboardPanelComponent
     editMode = false;
     timeRangeVisible = true;
 
-    @ViewChild('dashboardGrid')
-    dashboardGrid: DashboardGridViewComponent;
-
-    @ViewChild('dashboardSlide')
-    dashboardSlide: DashboardSlideViewComponent;
+    _dashboardGrid: DashboardGridViewComponent;
+    _dashboardSlide: DashboardSlideViewComponent;
 
     hasDataExplorerWritePrivileges = false;
 
@@ -133,11 +131,14 @@ export class DashboardPanelComponent
         dashboardItem.x = 0;
         dashboardItem.y = 0;
         this.dashboard.widgets.push(dashboardItem);
-        if (this.viewMode === 'grid') {
-            this.dashboardGrid.loadWidgetConfig(dataViewElementId, true);
-        } else {
-            this.dashboardSlide.loadWidgetConfig(dataViewElementId, true);
-        }
+        setTimeout(() => {
+            if (this.viewMode === 'grid') {
+                console.log(this.dashboardGrid);
+                this.dashboardGrid.loadWidgetConfig(dataViewElementId, true);
+            } else {
+                this.dashboardSlide.loadWidgetConfig(dataViewElementId, true);
+            }
+        });
     }
 
     setShouldShowConfirm(): boolean {
@@ -207,42 +208,49 @@ export class DashboardPanelComponent
     }
 
     getDashboard(dashboardId: string, startTime: number, endTime: number) {
-        this.dashboardService.getDashboard(dashboardId).subscribe(dashboard => 
{
-            this.dashboard = dashboard;
-            this.originalDashboard = JSON.parse(JSON.stringify(dashboard));
-            this.breadcrumbService.updateBreadcrumb(
-                this.breadcrumbService.makeRoute(
-                    [SpDashboardRoutes.BASE],
-                    this.dashboard.name,
-                ),
-            );
-            this.viewMode =
-                this.dashboard.dashboardGeneralSettings.defaultViewMode ||
-                'grid';
-            if (
-                this.dashboard.dashboardGeneralSettings.globalTimeEnabled ===
-                undefined
-            ) {
-                this.dashboard.dashboardGeneralSettings.globalTimeEnabled =
-                    true;
-            }
-            if (!this.dashboard.dashboardTimeSettings.startTime) {
-                this.dashboard.dashboardTimeSettings =
-                    this.timeSelectionService.getDefaultTimeSettings();
-            } else {
-                this.timeSelectionService.updateTimeSettings(
-                    this.timeSelectionService.defaultQuickTimeSelections,
-                    this.dashboard.dashboardTimeSettings,
-                    new Date(),
+        this.dashboardService
+            .getCompositeDashboard(dashboardId)
+            .subscribe(compositeDashboard => {
+                this.dashboard = compositeDashboard.dashboard;
+                this.widgets = compositeDashboard.widgets;
+                this.originalDashboard = JSON.parse(
+                    JSON.stringify(compositeDashboard.dashboard),
                 );
-            }
-            this.timeSettings =
-                startTime && endTime
-                    ? this.overrideTime(+startTime, +endTime)
-                    : this.dashboard.dashboardTimeSettings;
-            this.dashboardLoaded = true;
-            this.modifyRefreshInterval(this.dashboard.dashboardLiveSettings);
-        });
+                this.breadcrumbService.updateBreadcrumb(
+                    this.breadcrumbService.makeRoute(
+                        [SpDashboardRoutes.BASE],
+                        this.dashboard.name,
+                    ),
+                );
+                this.viewMode =
+                    this.dashboard.dashboardGeneralSettings.defaultViewMode ||
+                    'grid';
+                if (
+                    this.dashboard.dashboardGeneralSettings
+                        .globalTimeEnabled === undefined
+                ) {
+                    this.dashboard.dashboardGeneralSettings.globalTimeEnabled =
+                        true;
+                }
+                if (!this.dashboard.dashboardTimeSettings.startTime) {
+                    this.dashboard.dashboardTimeSettings =
+                        this.timeSelectionService.getDefaultTimeSettings();
+                } else {
+                    this.timeSelectionService.updateTimeSettings(
+                        this.timeSelectionService.defaultQuickTimeSelections,
+                        this.dashboard.dashboardTimeSettings,
+                        new Date(),
+                    );
+                }
+                this.timeSettings =
+                    startTime && endTime
+                        ? this.overrideTime(+startTime, +endTime)
+                        : this.dashboard.dashboardTimeSettings;
+                this.dashboardLoaded = true;
+                this.modifyRefreshInterval(
+                    this.dashboard.dashboardLiveSettings,
+                );
+            });
     }
 
     overrideTime(startTime: number, endTime: number): TimeSettings {
@@ -322,4 +330,26 @@ export class DashboardPanelComponent
             )
             .subscribe();
     }
+
+    @ViewChild('dashboardGrid', { static: false })
+    set dashboardGrid(v: DashboardGridViewComponent) {
+        if (v) {
+            this._dashboardGrid = v;
+        }
+    }
+
+    get dashboardGrid(): DashboardGridViewComponent {
+        return this._dashboardGrid;
+    }
+
+    @ViewChild('dashboardSlide', { static: false })
+    set dashboardSlide(v: DashboardSlideViewComponent) {
+        if (v) {
+            this._dashboardSlide = v;
+        }
+    }
+
+    get dashboardSlide(): DashboardSlideViewComponent {
+        return this._dashboardSlide;
+    }
 }
diff --git 
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
 
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
index 373774462a..fb13faf5bb 100644
--- 
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
+++ 
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
@@ -31,7 +31,7 @@ import {
 } from '@angular/core';
 import { GridsterItemComponent } from 'angular-gridster2';
 import {
-    DashboardItem,
+    ClientDashboardItem,
     DataExplorerWidgetModel,
     DataLakeMeasure,
     ExtendedTimeSettings,
@@ -70,7 +70,7 @@ export class DataExplorerChartContainerComponent
     @ViewChild('timeSelectorMenu')
     timeSelectorMenu: TimeRangeSelectorMenuComponent;
     @Input()
-    dashboardItem: DashboardItem;
+    dashboardItem: ClientDashboardItem;
 
     @Input()
     configuredWidget: DataExplorerWidgetModel;
diff --git 
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
 
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
index 5db3fea62a..508dc62ff5 100644
--- 
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
+++ 
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
@@ -28,11 +28,10 @@ import {
 import { GridsterItem, GridsterItemComponent } from 'angular-gridster2';
 import { ChartConfigurationService } from 
'../../../services/chart-configuration.service';
 import {
-    DashboardItem,
+    ClientDashboardItem,
     DataExplorerDataConfig,
     DataExplorerField,
     DataExplorerWidgetModel,
-    DatalakeRestService,
     DataViewQueryGeneratorService,
     SpLogMessage,
     SpQueryResult,
@@ -80,7 +79,7 @@ export abstract class BaseDataExplorerWidgetDirective<
     @Input()
     gridMode = true;
 
-    @Input() dataViewDashboardItem: DashboardItem;
+    @Input() dataViewDashboardItem: ClientDashboardItem;
     @Input() dataExplorerWidget: T;
 
     @Input()
@@ -110,8 +109,6 @@ export abstract class BaseDataExplorerWidgetDirective<
         Observable<SpQueryResult>[]
     >();
 
-    // inject services to avoid constructor overload
-    protected dataLakeRestService = inject(DatalakeRestService);
     protected widgetConfigurationService = inject(ChartConfigurationService);
     protected resizeService = inject(ResizeService);
     protected dataViewQueryGeneratorService = inject(
diff --git 
a/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
 
b/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
index 79875aae08..c66e0cbed2 100644
--- 
a/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
+++ 
b/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
@@ -16,11 +16,12 @@
  *
  */
 
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, inject, OnInit, ViewChild } from '@angular/core';
 import { MatSort } from '@angular/material/sort';
 import { BaseDataExplorerWidgetDirective } from 
'../base/base-data-explorer-widget.directive';
 import {
     DataExplorerField,
+    DatalakeRestService,
     SpQueryResult,
 } from '@streampipes/platform-services';
 import { ImageWidgetModel } from './model/image-widget.model';
@@ -45,9 +46,8 @@ export class ImageWidgetComponent
     canvasWidth;
     imagePreviewHeight;
 
-    constructor(private securePipe: SecurePipe) {
-        super();
-    }
+    private securePipe = inject(SecurePipe);
+    private dataLakeRestService = inject(DatalakeRestService);
 
     ngOnInit(): void {
         super.ngOnInit();
diff --git a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts 
b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
index ca0d5b9b62..57f4089b1b 100644
--- a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
+++ b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
@@ -22,7 +22,7 @@ import {
     GridsterItemComponent,
 } from 'angular-gridster2';
 import {
-    DashboardItem,
+    ClientDashboardItem,
     DataExplorerField,
     DataExplorerWidgetModel,
     SpLogMessage,
@@ -50,7 +50,7 @@ export interface BaseWidgetData<T extends 
DataExplorerWidgetModel> {
 
     timeSettings: TimeSettings;
 
-    dataViewDashboardItem: DashboardItem;
+    dataViewDashboardItem: ClientDashboardItem;
     dataExplorerWidget: T;
     previewMode: boolean;
     gridMode: boolean;


Reply via email to