diff --git a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/DynAttrEmbeddingStrategies.java b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/DynAttrEmbeddingStrategies.java index 9c2de7657c..c4419ce75a 100644 --- a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/DynAttrEmbeddingStrategies.java +++ b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/DynAttrEmbeddingStrategies.java @@ -35,7 +35,7 @@ public DynAttrEmbeddingStrategies(@Autowired(required = false) List owner) { + public void embedAttributes(Component component, Component owner) { if (embeddingStrategies != null) { for (EmbeddingStrategy strategy : embeddingStrategies) { if (strategy.supportComponent(component)) { diff --git a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/facet/DynAttrFacetImpl.java b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/facet/DynAttrFacetImpl.java index 1ecd0fd89b..59e2eef3c2 100644 --- a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/facet/DynAttrFacetImpl.java +++ b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/facet/DynAttrFacetImpl.java @@ -16,11 +16,14 @@ package io.jmix.dynattrflowui.facet; +import com.vaadin.flow.component.Composite; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.server.streams.DownloadHandler; import io.jmix.core.annotation.Internal; import io.jmix.dynattrflowui.impl.AttributeDefaultValues; +import io.jmix.flowui.facet.FacetOwner; import io.jmix.flowui.facet.impl.AbstractFacet; import io.jmix.flowui.view.StandardDetailView; -import io.jmix.flowui.view.View; import io.jmix.flowui.view.ViewControllerUtils; import org.springframework.lang.Nullable; @@ -34,19 +37,21 @@ public DynAttrFacetImpl(AttributeDefaultValues attributeDefaultValues) { } @Override - public void setOwner(@Nullable View owner) { + public & FacetOwner> void setOwner(@Nullable T owner) { super.setOwner(owner); subscribe(); } - private void subscribe() { - View view = getOwner(); - if (view == null) { - throw new IllegalStateException("DynAttrFacet is not attached to Frame"); + protected void subscribe() { + FacetOwner owner = getOwner(); + if (owner == null) { + throw new IllegalStateException("%s is not attached to owner" + .formatted(DynAttrFacet.class.getSimpleName())); } - if (view instanceof StandardDetailView) { - ViewControllerUtils.addInitEntityEventListener((StandardDetailView) view, e -> attributeDefaultValues.initDefaultAttributeValues(e.getEntity())); + if (owner instanceof StandardDetailView view) { + ViewControllerUtils.addInitEntityEventListener(view, + e -> attributeDefaultValues.initDefaultAttributeValues(e.getEntity())); } } } diff --git a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/BaseEmbeddingStrategy.java b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/BaseEmbeddingStrategy.java index d95f244148..a7ef3b55a1 100644 --- a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/BaseEmbeddingStrategy.java +++ b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/BaseEmbeddingStrategy.java @@ -24,9 +24,10 @@ import io.jmix.dynattr.*; import io.jmix.flowui.accesscontext.UiEntityAttributeContext; import io.jmix.flowui.accesscontext.UiEntityContext; +import io.jmix.flowui.component.UiComponentUtils; +import io.jmix.flowui.fragment.Fragment; +import io.jmix.flowui.fragment.FragmentUtils; import io.jmix.flowui.model.*; -import io.jmix.flowui.view.View; -import org.springframework.util.Assert; import java.util.Comparator; import java.util.List; @@ -53,30 +54,41 @@ protected BaseEmbeddingStrategy(Metadata metadata, protected abstract void setLoadDynamicAttributes(Component component); - protected abstract void embed(Component component, View owner, List attributes); + protected abstract void embed(Component component, List attributes); @Override - public void embed(Component component, View owner) { - if (getWindowId(owner) != null) { + public void embed(Component component, Component owner) { + if (getOwnerId(owner) != null) { MetaClass entityMetaClass = getEntityMetaClass(component); if (metadataTools.isJpaEntity(entityMetaClass)) { List attributes = findVisibleAttributes( entityMetaClass, - getWindowId(owner), component.getId().orElse("")); + getOwnerId(owner), UiComponentUtils.getComponentId(component).orElse("")); if (!attributes.isEmpty()) { setLoadDynamicAttributes(component); } - embed(component, owner, attributes); + embed(component, attributes); } } } - protected String getWindowId(View view) { - return view.getId().orElseThrow(); + protected String getOwnerId(Component owner) { + String ownerId = owner.getId() + .orElseThrow(() -> new IllegalStateException( + "Cannot embed dynamic attributes into a component without an ID")); + + if (owner instanceof Fragment fragment) { + ownerId = FragmentUtils.getHostView(fragment) + .getId() + .orElseThrow(() -> new IllegalStateException( + "Cannot embed dynamic attributes into a component with a host view without an ID")) + + "." + ownerId; + } + return ownerId; } protected void setLoadDynamicAttributes(InstanceContainer container) { diff --git a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/DataGridEmbeddingStrategy.java b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/DataGridEmbeddingStrategy.java index 3baa371faa..d9b88fd84c 100644 --- a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/DataGridEmbeddingStrategy.java +++ b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/DataGridEmbeddingStrategy.java @@ -17,7 +17,6 @@ package io.jmix.dynattrflowui.impl; import com.vaadin.flow.component.Component; -import com.vaadin.flow.data.renderer.Renderer; import io.jmix.core.AccessManager; import io.jmix.core.DataManager; import io.jmix.core.Metadata; @@ -32,7 +31,6 @@ import io.jmix.flowui.component.grid.DataGrid; import io.jmix.flowui.data.EntityDataUnit; import io.jmix.flowui.data.grid.ContainerDataGridItems; -import io.jmix.flowui.view.View; import java.util.List; @@ -57,7 +55,7 @@ public boolean supportComponent(Component component) { } @Override - protected void embed(Component component, View owner, List attributes) { + protected void embed(Component component, List attributes) { DataGrid dataGrid = (DataGrid) component; for (AttributeDefinition attribute : attributes) { addAttributeColumn(dataGrid, attribute); diff --git a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/EmbeddingStrategy.java b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/EmbeddingStrategy.java index 2429808b1a..d5dd4ffc08 100644 --- a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/EmbeddingStrategy.java +++ b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/EmbeddingStrategy.java @@ -17,11 +17,10 @@ package io.jmix.dynattrflowui.impl; import com.vaadin.flow.component.Component; -import io.jmix.flowui.view.View; public interface EmbeddingStrategy { boolean supportComponent(Component component); - void embed(Component component, View owner); + void embed(Component component, Component owner); } diff --git a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/FormEmbeddingStrategy.java b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/FormEmbeddingStrategy.java index 72c6e6ac12..00c8f984ae 100644 --- a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/FormEmbeddingStrategy.java +++ b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/impl/FormEmbeddingStrategy.java @@ -34,7 +34,6 @@ import io.jmix.flowui.data.ValueSource; import io.jmix.flowui.data.ValueSourceProvider; import io.jmix.flowui.data.value.ContainerValueSourceProvider; -import io.jmix.flowui.view.View; import org.springframework.util.Assert; import java.util.List; @@ -65,7 +64,7 @@ public boolean supportComponent(Component component) { } @Override - protected void embed(Component component, View view, List attributes) { + protected void embed(Component component, List attributes) { FormLayout form = (FormLayout) component; for (AttributeDefinition attribute : attributes) { addAttributeComponent((JmixFormLayout) form, attribute); diff --git a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/xml/facet/loader/DynAttrFacetLoader.java b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/xml/facet/loader/DynAttrFacetLoader.java index 8f96d2197d..31b2467907 100644 --- a/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/xml/facet/loader/DynAttrFacetLoader.java +++ b/jmix-dynattr/dynattr-flowui/src/main/java/io/jmix/dynattrflowui/xml/facet/loader/DynAttrFacetLoader.java @@ -16,13 +16,14 @@ package io.jmix.dynattrflowui.xml.facet.loader; +import com.vaadin.flow.component.Component; import io.jmix.dynattrflowui.DynAttrEmbeddingStrategies; import io.jmix.dynattrflowui.facet.DynAttrFacet; import io.jmix.flowui.component.UiComponentUtils; import io.jmix.flowui.impl.FacetsImpl; -import io.jmix.flowui.view.View; import io.jmix.flowui.xml.facet.FacetProvider; import io.jmix.flowui.xml.facet.loader.AbstractFacetLoader; +import io.jmix.flowui.xml.layout.ComponentLoader; public class DynAttrFacetLoader extends AbstractFacetLoader { @@ -39,17 +40,16 @@ public void loadFacet() { if (facets instanceof FacetsImpl facetsImpl) { FacetProvider provider = facetsImpl.getProvider(DynAttrFacet.class); - if (provider != null) { - provider.loadFromXml(resultFacet, element, context); + if (provider != null && context instanceof ComponentLoader.ComponentContext componentContext) { + provider.loadFromXml(resultFacet, element, componentContext); return; } } - View view = context.getView(); - context.addInitTask(__ -> { - UiComponentUtils.traverseComponents(view, component -> - getEmbeddingStrategies().embedAttributes(component, view)); - }); + Component owner = context.getOrigin(); + context.addInitTask(__ -> + UiComponentUtils.traverseComponents(owner, component -> + getEmbeddingStrategies().embedAttributes(component, owner))); } protected DynAttrEmbeddingStrategies getEmbeddingStrategies() { diff --git a/jmix-flowui/flowui-data/src/test/java/test_support/AbstractSettingsTest.java b/jmix-flowui/flowui-data/src/test/java/test_support/AbstractSettingsTest.java index 28fd0a198e..c43c341fd8 100644 --- a/jmix-flowui/flowui-data/src/test/java/test_support/AbstractSettingsTest.java +++ b/jmix-flowui/flowui-data/src/test/java/test_support/AbstractSettingsTest.java @@ -17,8 +17,8 @@ package test_support; import io.jmix.core.security.SystemAuthenticator; +import io.jmix.flowui.facet.settings.ComponentSettingsManager; import io.jmix.flowui.facet.settings.ViewSettings; -import io.jmix.flowui.facet.settings.ViewSettingsComponentManager; import io.jmix.flowui.facet.settings.ViewSettingsJson; import io.jmix.flowui.settings.UserSettingsService; import io.jmix.flowui.testassist.UiTestUtils; @@ -37,7 +37,7 @@ public abstract class AbstractSettingsTest { @Autowired protected SystemAuthenticator authenticator; @Autowired - protected ViewSettingsComponentManager settingsManager; + protected ComponentSettingsManager settingsManager; @Autowired protected ViewNavigationSupport navigationSupport; diff --git a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioDataElements.java b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioDataElements.java index 6319b04a5d..75b6026bad 100644 --- a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioDataElements.java +++ b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/element/StudioDataElements.java @@ -164,6 +164,18 @@ public interface StudioDataElements { ) void onViewEventLoadTrigger(); + @StudioElement( + name = "OnFragmentEventLoadTrigger", + classFqn = "io.jmix.flowui.facet.dataloadcoordinator.OnFragmentEventLoadTrigger", + xmlElement = "onFragmentEvent", + icon = "io/jmix/flowui/kit/meta/icon/element/onFragmentEventLoadTrigger.svg", + properties = { + @StudioProperty(xmlAttribute = "type", type = StudioPropertyType.ENUMERATION, + options = {"Ready", "Host.Init", "Host.BeforeShow", "Host.Ready"}, required = true) + } + ) + void onFragmentEventLoadTrigger(); + @StudioElement( name = "OnComponentValueChangedLoadTrigger", classFqn = "io.jmix.flowui.facet.dataloadcoordinator.OnComponentValueChangedLoadTrigger", diff --git a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/facet/StudioFacets.java b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/facet/StudioFacets.java index a5ac67716e..5508c15110 100644 --- a/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/facet/StudioFacets.java +++ b/jmix-flowui/flowui-kit/src/main/java/io/jmix/flowui/kit/meta/facet/StudioFacets.java @@ -26,7 +26,7 @@ public interface StudioFacets { @StudioFacet( name = "DataLoadCoordinator", - classFqn = "io.jmix.flowui.facet.DataLoadCoordinator", + classFqn = "io.jmix.flowui.facet.ViewDataLoadCoordinator", category = "Facets", xmlElement = "dataLoadCoordinator", icon = "io/jmix/flowui/kit/meta/icon/facet/dataLoadCoordinator.svg", @@ -43,6 +43,25 @@ public interface StudioFacets { ) void dataLoadCoordinator(); + @StudioFacet( + name = "FragmentDataLoadCoordinator", + classFqn = "io.jmix.flowui.facet.FragmentDataLoadCoordinator", + category = "Facets", + xmlElement = "fragmentDataLoadCoordinator", + icon = "io/jmix/flowui/kit/meta/icon/facet/dataLoadCoordinator.svg", + documentationLink = "%VERSION%/flow-ui/facets/dataLoadCoordinator.html", + properties = { + @StudioProperty(xmlAttribute = "id", type = StudioPropertyType.COMPONENT_ID), + @StudioProperty(xmlAttribute = "auto", type = StudioPropertyType.BOOLEAN, + defaultValue = "false", initialValue = "true"), + @StudioProperty(xmlAttribute = "componentPrefix", type = StudioPropertyType.STRING, + defaultValue = "component_"), + @StudioProperty(xmlAttribute = "containerPrefix", type = StudioPropertyType.STRING, + defaultValue = "container_"), + } + ) + void fragmentDataLoadCoordinator(); + @StudioFacet( name = "UrlQueryParameters", classFqn = "io.jmix.flowui.facet.UrlQueryParametersFacet", @@ -74,7 +93,7 @@ public interface StudioFacets { @StudioFacet( name = "Settings", - classFqn = "io.jmix.flowui.facet.SettingsFacet", + classFqn = "io.jmix.flowui.facet.ViewSettingsFacet", category = "Facets", xmlElement = "settings", icon = "io/jmix/flowui/kit/meta/icon/facet/settings.svg", @@ -85,4 +104,18 @@ public interface StudioFacets { } ) void settings(); + + @StudioFacet( + name = "FragmentSettings", + classFqn = "io.jmix.flowui.facet.FragmentSettingsFacet", + category = "Facets", + xmlElement = "fragmentSettings", + icon = "io/jmix/flowui/kit/meta/icon/facet/settings.svg", + properties = { + @StudioProperty(xmlAttribute = "id", type = StudioPropertyType.COMPONENT_ID), + @StudioProperty(xmlAttribute = "auto", type = StudioPropertyType.BOOLEAN, + defaultValue = "false", initialValue = "true"), + } + ) + void fragmentSettings(); } diff --git a/jmix-flowui/flowui-kit/src/main/resources/io/jmix/flowui/kit/meta/icon/element/onFragmentEventLoadTrigger.svg b/jmix-flowui/flowui-kit/src/main/resources/io/jmix/flowui/kit/meta/icon/element/onFragmentEventLoadTrigger.svg new file mode 100644 index 0000000000..6c0da22250 --- /dev/null +++ b/jmix-flowui/flowui-kit/src/main/resources/io/jmix/flowui/kit/meta/icon/element/onFragmentEventLoadTrigger.svg @@ -0,0 +1,19 @@ + + + + + diff --git a/jmix-flowui/flowui-kit/src/main/resources/io/jmix/flowui/kit/meta/icon/element/onFragmentEventLoadTrigger_dark.svg b/jmix-flowui/flowui-kit/src/main/resources/io/jmix/flowui/kit/meta/icon/element/onFragmentEventLoadTrigger_dark.svg new file mode 100644 index 0000000000..39b166a6a2 --- /dev/null +++ b/jmix-flowui/flowui-kit/src/main/resources/io/jmix/flowui/kit/meta/icon/element/onFragmentEventLoadTrigger_dark.svg @@ -0,0 +1,19 @@ + + + + + diff --git a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/Fragments.java b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/Fragments.java index 55165e7509..f708f78f0d 100644 --- a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/Fragments.java +++ b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/Fragments.java @@ -20,6 +20,7 @@ import io.jmix.flowui.fragment.FragmentDescriptor; import io.jmix.flowui.fragment.FragmentOwner; import io.jmix.flowui.xml.layout.ComponentLoader; +import org.springframework.lang.Nullable; /** @@ -43,6 +44,26 @@ public interface Fragments { */ > F create(FragmentOwner parent, Class fragmentClass); + /** + * Creates a fragment instance by its controller class. + *

+ * For example: + *

{@code
+     *    AddressFragment addressFragment = fragments.create(this, AddressFragment.class, "addressFragment");
+     *    getContent().add(addressFragment);
+     * }
+ * + * @param parent parent UI controller + * @param fragmentClass fragment controller class + * @param fragmentId id that will be set to the fragment + * @param fragment type + * @return fully initialized fragment instance + */ + default > F create(FragmentOwner parent, Class fragmentClass, @Nullable String fragmentId) { + // ignore ID by default for backward compatibility + return create(parent, fragmentClass); + } + /** * Initializes passed fragment by processing {@link FragmentDescriptor}. * diff --git a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/action/genericfilter/GenericFilterMakeDefaultAction.java b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/action/genericfilter/GenericFilterMakeDefaultAction.java index 13ed8c121b..8834397b99 100644 --- a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/action/genericfilter/GenericFilterMakeDefaultAction.java +++ b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/action/genericfilter/GenericFilterMakeDefaultAction.java @@ -19,14 +19,15 @@ import io.jmix.core.Messages; import io.jmix.core.common.util.Preconditions; import io.jmix.flowui.action.ActionType; +import io.jmix.flowui.component.UiComponentUtils; +import io.jmix.flowui.component.genericfilter.FilterUtils; import io.jmix.flowui.component.genericfilter.GenericFilter; import io.jmix.flowui.component.genericfilter.model.FilterConfigurationModel; import io.jmix.flowui.facet.SettingsFacet; -import io.jmix.flowui.facet.settings.ViewSettings; +import io.jmix.flowui.facet.settings.UiComponentSettings; import io.jmix.flowui.facet.settings.component.GenericFilterSettings; import io.jmix.flowui.icon.Icons; import io.jmix.flowui.kit.icon.JmixFontIcon; -import io.jmix.flowui.view.ViewControllerUtils; import org.springframework.beans.factory.annotation.Autowired; /** @@ -62,9 +63,9 @@ protected void setIcons(Icons icons) { @Override protected boolean isApplicable() { return super.isApplicable() - && target.getId().isPresent() + && UiComponentUtils.getComponentId(target).isPresent() && target.isAttached() - && ViewControllerUtils.getViewFacet(getParentView(), SettingsFacet.class) != null + && FilterUtils.getFacet(target, SettingsFacet.class) != null && target.getCurrentConfiguration() != target.getEmptyConfiguration(); } @@ -72,16 +73,16 @@ protected boolean isApplicable() { public void execute() { checkTarget(); - SettingsFacet settingsFacet = ViewControllerUtils.getViewFacet(getParentView(), SettingsFacet.class); + SettingsFacet settingsFacet = FilterUtils.getFacet(target, SettingsFacet.class); Preconditions.checkNotNullArgument(settingsFacet, "The view doesn't contain %s", SettingsFacet.class.getSimpleName()); - ViewSettings settings = settingsFacet.getSettings(); + UiComponentSettings settings = settingsFacet.getSettings(); Preconditions.checkNotNullArgument(settings, "%s isn't attached to the view", SettingsFacet.class.getSimpleName()); GenericFilterSettings genericFilterSettings = new GenericFilterSettings(); - genericFilterSettings.setId(target.getId().orElse(null)); + genericFilterSettings.setId(UiComponentUtils.getComponentId(target).orElse(null)); genericFilterSettings.setDefaultConfigurationId(target.getCurrentConfiguration().getId()); settings.put(genericFilterSettings); } diff --git a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/HasFacetsComponents.java b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/HasFacetsComponents.java new file mode 100644 index 0000000000..61742a0da7 --- /dev/null +++ b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/HasFacetsComponents.java @@ -0,0 +1,59 @@ +/* + * Copyright 2025 Haulmont. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jmix.flowui.component; + +import io.jmix.flowui.facet.Facet; +import org.springframework.lang.Nullable; + +import java.util.stream.Stream; + +/** + * Interface defining methods for managing facet API elements. + */ +public interface HasFacetsComponents { + + /** + * Adds a facet to a view. + * + * @param facet the facet to be added + */ + void addFacet(Facet facet); + + /** + * Returns a facet by its ID. + * + * @param id the identifier of the facet to retrieve + * @return the facet corresponding to the given identifier, + * or {@code null} if no facet is associated with the identifier + */ + @Nullable + Facet getFacet(String id); + + /** + * Removes the specified facet from the view. + * + * @param facet the facet to be removed + */ + void removeFacet(Facet facet); + + /** + * Returns a stream of all facets associated with the view. + * + * @return a stream of {@link Facet} instances associated with the view + */ + Stream getFacets(); +} diff --git a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/UiComponentUtils.java b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/UiComponentUtils.java index a4ababce86..312b23bb4e 100644 --- a/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/UiComponentUtils.java +++ b/jmix-flowui/flowui/src/main/java/io/jmix/flowui/component/UiComponentUtils.java @@ -22,6 +22,7 @@ import com.vaadin.flow.component.page.PendingJavaScriptResult; import com.vaadin.flow.component.shared.HasPrefix; import com.vaadin.flow.component.shared.HasSuffix; +import com.vaadin.flow.server.Attributes; import com.vaadin.flow.server.streams.DownloadHandler; import com.vaadin.flow.server.streams.DownloadResponse; import io.jmix.core.FileRef; @@ -81,16 +82,16 @@ public static Optional findComponent(View view, String id) { } /** - * Returns the component with given id. + * Returns the component with the given id. * - * @param view view to find component from - * @param id component id + * @param owner view or fragment to find component from + * @param id component id * @return the component with given id - * @throws IllegalStateException if view content is not a container - * @throws IllegalArgumentException if a component with given id is not found + * @throws IllegalStateException if the owner content is not a container + * @throws IllegalArgumentException if a component with the given id is not found */ - public static Component getComponent(View view, String id) { - return findComponent(view, id) + public static Component getComponent(Composite owner, String id) { + return findComponent(owner, id) .orElseThrow(() -> new IllegalArgumentException( String.format("Component with id '%s' not found", id))); } @@ -228,7 +229,7 @@ public static Collection getOwnComponents(Component container) { return ((ComponentContainer) container).getOwnComponents(); } else if (container instanceof HasComponents) { return container.getChildren().sequential().collect(Collectors.toList()); - } else if (container instanceof View) { + } else if (container instanceof View || container instanceof Fragment) { return container.getChildren().collect(Collectors.toList()); } else { throw new IllegalArgumentException(container.getClass().getSimpleName() + @@ -540,6 +541,25 @@ public static Fragment findFragment(Component component) { return parent.map(UiComponentUtils::findFragment).orElse(null); } + /** + * Gets the id of the root element of this component. + *

+ * Gets the id of the root element of this component, depending on the attachment context: + *