You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2026/06/19 11:06:42 UTC

(myfaces-tobago) branch main updated: feature(dropdown): JavaScript events as behavior

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

henningn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/main by this push:
     new e53627e3e6 feature(dropdown): JavaScript events as behavior
e53627e3e6 is described below

commit e53627e3e67e466cc13cb425d5dadf8d2530a098
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Fri Jun 19 08:17:07 2026 +0200

    feature(dropdown): JavaScript events as behavior
    
    The JavaScript event should be on tc:in instead of tc:suggest, because an Ajax request replaces the tobago-suggest element. If a listener is put on tobago-suggest, it is outdated after an Ajax request and a new listener must be set.
    
    Issue: TOBAGO-2526
---
 .../renderkit/renderer/SuggestRenderer.java         |  2 --
 .../internal/taglib/component/InTagDeclaration.java | 10 +++++++++-
 .../taglib/component/SuggestTagDeclaration.java     | 13 -------------
 .../z-misc/dropdown/behavior/Behavior.xhtml         |  2 +-
 .../z-misc/dropdown/behavior/stop-hide-event.js     | 12 +-----------
 .../z-misc/dropdown/behavior/stop-show-event.js     | 12 +-----------
 .../src/main/ts/tobago-dropdown-menu.ts             |  8 --------
 .../src/main/ts/tobago-suggest.ts                   | 21 ++++++++++-----------
 8 files changed, 22 insertions(+), 58 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SuggestRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SuggestRenderer.java
index f4ae349ff6..f884802600 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SuggestRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SuggestRenderer.java
@@ -122,8 +122,6 @@ public void encodeBeginInternal(final FacesContext facesContext, final T compone
     writer.writeAttribute(HtmlAttributes.NAME, clientId, false);
     writer.endElement(HtmlElements.INPUT);
 
-    encodeBehavior(writer, facesContext, component);
-
     writer.endElement(HtmlElements.TOBAGO_SUGGEST);
   }
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/InTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/InTagDeclaration.java
index 0b2ef171ae..ab4ca02dca 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/InTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/InTagDeclaration.java
@@ -95,7 +95,15 @@
         @Behavior(
             name = ClientBehaviors.KEYUP),
         @Behavior(
-            name = ClientBehaviors.BLUR)
+            name = ClientBehaviors.BLUR),
+        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_HIDE, description = "Event is fired before the suggest"
+            + " dropdown menu is hidden. Behavior only applies if a tc:suggest is nested."),
+        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_HIDDEN, description = "Event is fired after the suggest"
+            + " dropdown menu is hidden. Behavior only applies if a tc:suggest is nested."),
+        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_SHOW, description = "Event is fired before the suggest"
+            + " dropdown menu is shown. Behavior only applies if a tc:suggest is nested."),
+        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_SHOWN, description = "Event is fired after the suggest"
+            + " dropdown menu is shown. Behavior only applies if a tc:suggest is nested.")
     },
     markups = {
         @Markup(
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SuggestTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SuggestTagDeclaration.java
index 488b922c0f..920b525794 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SuggestTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SuggestTagDeclaration.java
@@ -19,12 +19,10 @@
 
 package org.apache.myfaces.tobago.internal.taglib.component;
 
-import org.apache.myfaces.tobago.apt.annotation.Behavior;
 import org.apache.myfaces.tobago.apt.annotation.Tag;
 import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
 import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
 import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
-import org.apache.myfaces.tobago.component.ClientBehaviors;
 import org.apache.myfaces.tobago.component.RendererTypes;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasIdBindingAndRendered;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasLocalMenu;
@@ -54,17 +52,6 @@
     allowedChildComponents = {
         "org.apache.myfaces.tobago.SelectItems",
         "org.apache.myfaces.tobago.SelectItem"
-    },
-    behaviors = {
-        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_HIDE, description = "Event is fired before a dropdown menu is"
-            + " hidden. Behavior only applies if the button toggles a dropdown menu."),
-        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_HIDDEN, description = "Event is fired after a dropdown menu is"
-            + " hidden. Behavior only applies if the button toggles a dropdown menu.",
-            isDefault = true),
-        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_SHOW, description = "Event is fired before a dropdown menu is"
-            + " shown. Behavior only applies if the button toggles a dropdown menu."),
-        @Behavior(name = ClientBehaviors.TOBAGO_DROPDOWN_SHOWN, description = "Event is fired after a dropdown menu is"
-            + " shown. Behavior only applies if the button toggles a dropdown menu.")
     })
 public interface SuggestTagDeclaration extends HasIdBindingAndRendered, HasLocalMenu {
 
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/Behavior.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/Behavior.xhtml
index 92f0e3aa84..7a80cca8fe 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/Behavior.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/Behavior.xhtml
@@ -53,8 +53,8 @@
   </tc:link>
 
   <tc:in id="suggestInput" label="Suggest">
+    <f:ajax event="tobago.dropdown.hidden" render="counter" listener="#{testBehaviorController.increaseSuggest}"/>
     <tc:suggest id="suggest" query="#{suggestController.query}">
-      <f:ajax event="tobago.dropdown.hidden" render="counter" listener="#{testBehaviorController.increaseSuggest}"/>
       <tc:selectItems value="#{suggestController.solarObjects}" var="name" itemValue="#{name}"/>
     </tc:suggest>
   </tc:in>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-hide-event.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-hide-event.js
index a92c8a9188..f1d88cc57c 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-hide-event.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-hide-event.js
@@ -23,20 +23,10 @@ function stopHideEvent(id) {
   });
 }
 
-function observeTobagoSuggest() {
-  const observer = new MutationObserver((mutations) => {
-    for (const mutation of mutations) {
-      stopHideEvent("page:mainForm:suggest");
-    }
-  });
-  observer.observe(document.getElementById("page:mainForm:suggest").parentElement, {childList: true});
-}
-
 document.addEventListener("DOMContentLoaded", function (event) {
   stopHideEvent("page:mainForm:button");
   stopHideEvent("page:mainForm:link");
-  stopHideEvent("page:mainForm:suggest");
+  stopHideEvent("page:mainForm:suggestInput");
   stopHideEvent("page:mainForm:selectOneList");
   stopHideEvent("page:mainForm:selectManyList");
-  observeTobagoSuggest();
 });
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-show-event.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-show-event.js
index e2d681b72e..ca9fac0407 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-show-event.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/z-misc/dropdown/behavior/stop-show-event.js
@@ -23,20 +23,10 @@ function stopShowEvent(id) {
   });
 }
 
-function observeTobagoSuggest() {
-  const observer = new MutationObserver((mutations) => {
-    for (const mutation of mutations) {
-      stopShowEvent("page:mainForm:suggest");
-    }
-  });
-  observer.observe(document.getElementById("page:mainForm:suggest").parentElement, {childList: true});
-}
-
 document.addEventListener("DOMContentLoaded", function (event) {
   stopShowEvent("page:mainForm:button");
   stopShowEvent("page:mainForm:link");
-  stopShowEvent("page:mainForm:suggest");
+  stopShowEvent("page:mainForm:suggestInput");
   stopShowEvent("page:mainForm:selectOneList");
   stopShowEvent("page:mainForm:selectManyList");
-  observeTobagoSuggest();
 });
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts
index bd9f19ac0d..642eee1e91 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts
@@ -54,14 +54,6 @@ export class DropdownMenu {
     this.alignment = alignment;
   }
 
-  /**
-   * Update event elements after a DOM change (Ajax request).
-   * @param eventElements element or list of elements for which the show/shown/hide/hidden event is dispatched
-   */
-  public updateEventElements(eventElements: HTMLElement | HTMLElement[]) {
-    this.eventElements = Array.isArray(eventElements) ? eventElements : [eventElements];
-  }
-
   private resizeEventListenerEvent(event: Event): void {
     this.updatePosition();
   }
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-suggest.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-suggest.ts
index bb364a9ef5..ab5da2c214 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-suggest.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-suggest.ts
@@ -27,6 +27,7 @@ import {OptionsControls} from "./tobago-options-controls";
 export class Suggest {
   private listeners: EventListenerStore = new EventListenerStore();
   private tobagoIn: HTMLElement;
+  private tobagoInField: HTMLInputElement;
   private dropdownMenu: DropdownMenu;
   private spinner: Spinner;
   private optionsControls: OptionsControls;
@@ -34,6 +35,7 @@ export class Suggest {
 
   constructor(tobagoIn: HTMLElement) {
     this.tobagoIn = tobagoIn;
+    this.tobagoInField = tobagoIn.querySelector("input[name='" + tobagoIn.id + "']");
   }
 
   public init(): void {
@@ -57,7 +59,7 @@ export class Suggest {
     /* eslint-enable max-len */
     this.optionsControls = new OptionsControls(this.optionsElement, this.select.bind(this));
 
-    this.dropdownMenu = new DropdownMenu(this.dropdownMenuElement, this.inputField, [this.tobagoIn, this.tobagoSuggest],
+    this.dropdownMenu = new DropdownMenu(this.dropdownMenuElement, this.inputField, [this.tobagoIn, this.tobagoInField],
         this.localMenu, DropdownMenuAlignment.centerFullWidth);
 
     this.inputField.role = "combobox";
@@ -239,17 +241,14 @@ export class Suggest {
 
   private ajaxEvent(event: faces.AjaxEvent): void {
     if (event.status === "success") {
-      setTimeout(() => { //use setTimeout with 1 ms to give time to reregister listeners to the tobago-suggest element
-        this.dropdownMenu.updateEventElements([this.tobagoIn, this.tobagoSuggest]);
-        this.optionsControls.renderRows(this.items);
-        this.spinner.hide();
+      this.optionsControls.renderRows(this.items);
+      this.spinner.hide();
 
-        if (this.items.length > 0) {
-          this.dropdownMenu.show();
-        } else {
-          this.dropdownMenu.hide();
-        }
-      }, 1);
+      if (this.items.length > 0) {
+        this.dropdownMenu.show();
+      } else {
+        this.dropdownMenu.hide();
+      }
     }
   }