From 42f14cb666316f87f853535ef7b8c87718c9aa15 Mon Sep 17 00:00:00 2001 From: marcin <106965834+MarcinVaadin@users.noreply.github.com> Date: Thu, 28 Jul 2022 12:29:49 +0200 Subject: [PATCH] feat: Provide collection based append/remove methods for Node (#14204) Added Collection based appendChild / appendVirtualChild / insertChild / removeChild / removeVirtualChild methods for com.vaadin.flow.dom.Node Fixes #13599 --- .../main/java/com/vaadin/flow/dom/Node.java | 116 +++++++++++++++++- 1 file changed, 111 insertions(+), 5 deletions(-) diff --git a/flow-server/src/main/java/com/vaadin/flow/dom/Node.java b/flow-server/src/main/java/com/vaadin/flow/dom/Node.java index eb5f1f47c49..25c7a49a7ba 100644 --- a/flow-server/src/main/java/com/vaadin/flow/dom/Node.java +++ b/flow-server/src/main/java/com/vaadin/flow/dom/Node.java @@ -16,7 +16,11 @@ package com.vaadin.flow.dom; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Objects; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -42,6 +46,8 @@ public abstract class Node> implements Serializable { static final String THE_CHILDREN_ARRAY_CANNOT_BE_NULL = "The children array cannot be null"; + static final String THE_CHILDREN_COLLECTION_CANNOT_BE_NULL = "The children collection cannot be null"; + private final ElementStateProvider stateProvider; private final StateNode node; @@ -138,6 +144,22 @@ public N appendChild(Element... children) { THE_CHILDREN_ARRAY_CANNOT_BE_NULL); } + return appendChild(Arrays.asList(children)); + } + + /** + * Adds the given children as the last children of this element. + * + * @param children + * the element(s) to add + * @return this element + */ + public N appendChild(Collection children) { + if (children == null) { + throw new IllegalArgumentException( + THE_CHILDREN_COLLECTION_CANNOT_BE_NULL); + } + insertChild(getChildCount(), children); return getSelf(); @@ -147,8 +169,8 @@ public N appendChild(Element... children) { * Appends the given children as the virtual children of the element. *

* The virtual child is not really a child of the DOM element. The - * client-side counterpart is created in the memory but it's not attached to - * the DOM tree. The resulting element is referenced via the server side + * client-side counterpart is created in the memory, but it's not attached + * to the DOM tree. The resulting element is referenced via the server side * {@link Element} in JS function call as usual. * * @param children @@ -161,6 +183,27 @@ public N appendVirtualChild(Element... children) { THE_CHILDREN_ARRAY_CANNOT_BE_NULL); } + return appendVirtualChild(Arrays.asList(children)); + } + + /** + * Appends the given children as the virtual children of the element. + *

+ * The virtual child is not really a child of the DOM element. The + * client-side counterpart is created in the memory, but it's not attached + * to the DOM tree. The resulting element is referenced via the server side + * {@link Element} in JS function call as usual. + * + * @param children + * the element(s) to add + * @return this element + */ + public N appendVirtualChild(Collection children) { + if (children == null) { + throw new IllegalArgumentException( + THE_CHILDREN_COLLECTION_CANNOT_BE_NULL); + } + for (Element child : children) { if (child == null) { throw new IllegalArgumentException( @@ -204,6 +247,33 @@ public N removeVirtualChild(Element... children) { THE_CHILDREN_ARRAY_CANNOT_BE_NULL); } + return removeVirtualChild(Arrays.asList(children)); + } + + /** + * Removes the given children that have been attached as the virtual + * children of this element. + *

+ * The virtual child is not really a child of the DOM element. The + * client-side counterpart is created in the memory but it's not attached to + * the DOM tree. The resulting element is referenced via the server side + * {@link Element} in JS function call as usual. * + * + * @param children + * the element(s) to remove + * @return this element + */ + /* + * The use case for removing virtual children is when exported Flow web + * components are detached from their parent due to missing heart beats + + * timeout. + */ + public N removeVirtualChild(Collection children) { + if (children == null) { + throw new IllegalArgumentException( + THE_CHILDREN_COLLECTION_CANNOT_BE_NULL); + } + if (getNode().hasFeature(VirtualChildrenList.class)) { VirtualChildrenList childrenList = getNode() .getFeature(VirtualChildrenList.class); @@ -266,15 +336,36 @@ public N insertChild(int index, Element... children) { throw new IllegalArgumentException( THE_CHILDREN_ARRAY_CANNOT_BE_NULL); } + + return insertChild(index, Arrays.asList(children)); + } + + /** + * Inserts the given child element(s) at the given position. + * + * @param index + * the position at which to insert the new child + * @param children + * the child element(s) to insert + * @return this element + */ + public N insertChild(int index, Collection children) { + if (children == null) { + throw new IllegalArgumentException( + THE_CHILDREN_COLLECTION_CANNOT_BE_NULL); + } if (index > getChildCount()) { throw new IllegalArgumentException(String.format( CANNOT_X_WITH_INDEX_Y_WHEN_THERE_ARE_Z_CHILDREN, "insert", index, getChildCount())); } - for (int i = 0, - insertIndex = index; i < children.length; i++, insertIndex++) { - Element child = children[i]; + List childrenList = children instanceof List + ? (List) children + : new ArrayList<>(children); + for (int i = 0, insertIndex = index; i < childrenList + .size(); i++, insertIndex++) { + Element child = childrenList.get(i); if (child == null) { throw new IllegalArgumentException( "Element to insert must not be null"); @@ -372,6 +463,21 @@ public N removeChild(Element... children) { THE_CHILDREN_ARRAY_CANNOT_BE_NULL); } + return removeChild(Arrays.asList(children)); + } + + /** + * Removes the given child element(s). + * + * @param children + * the child element(s) to remove + * @return this element + */ + public N removeChild(Collection children) { + if (children == null) { + throw new IllegalArgumentException( + THE_CHILDREN_COLLECTION_CANNOT_BE_NULL); + } for (Element child : children) { ensureChildHasParent(child, false); getStateProvider().removeChild(getNode(), child);