Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce getListeners(eventType) method on com.vaadin.flow.component.ComponentEventBus #9766

Closed
retomerz opened this issue Jan 7, 2021 · 5 comments · Fixed by #14209
Closed

Comments

@retomerz
Copy link

retomerz commented Jan 7, 2021

With Vaadin 8.x it was possible to use com.vaadin.server.AbstractClientConnector#getListeners

It is not possible to access the listeners with Vaadin Flow without reflection.

Please re-introduce a getListeners(eventType) method on com.vaadin.flow.component.ComponentEventBus
in Vaadin Flow 14.x

In our case, we need it to record user interaction with a component.
For example a click via click listener.
For this, every component will add an "internal" click listener.
On layouts (like VerticalLayout) the interaction is only recorded if another click listener (!= internal) is registered.
So basically the internal click listener checks for "getListeners(ClickEvent.class) > 1" on click event.
And only if more than one click listener is added, the click is recorded.

What we already have tested:
Temporary remove our internal click listener and then call "hasListener".
But the problem is that this will change the listener order.
Our internal click listener must be always fired as the first.

Another possible solution would be to override all add(Click)Listener methods and track it by our self.
But that feels a bit poor

@pleku
Copy link
Contributor

pleku commented Oct 7, 2021

@retomerz
Copy link
Author

retomerz commented Apr 7, 2022

Any news on this?

We also need to be able to check if a KeyEventListener exists for a specific key.
This was also possible in Vaadin 8, like this:

  public class MyTextField extends TextField {
      public boolean hasShortcutListener(int keyCode) {
          Action[] actions = getActionManager().getActions(null, null);
          return Arrays
                  .stream(actions)
                  .filter(action -> action instanceof ShortcutAction)
                  .map(action -> (ShortcutAction) action)
                  .anyMatch(action -> action.getKeyCode() == keyCode);
      }
  }

With Vaadin Flow the only way I see - without reflection - is to overwrite addKeyPressListener.
This feels weired since the returned Registration must be also wrapped.
The code would look like this (untested code, without 'modifiers' support):

public class MyTextField extends TextField {
  private Map<Key, Integer> keyCounts = new HashMap<>();

  @Override
  public Registration addKeyPressListener(Key key, ComponentEventListener<KeyPressEvent> listener, KeyModifier... modifiers) {
    Registration reg = super.addKeyPressListener(key, listener, modifiers);
    keyCounts.merge(key, 1, Integer::sum);
    return () -> {
      Integer count = keyCounts.get(key);
      count = count - 1;
      if (count < 1) {
        keyCounts.remove(key);
      } else {
        keyCounts.put(key, count);
      }
      reg.remove();
    };
  }

  public boolean hasKeyPressListener(Key key) {
    return keyCounts.get(key) != null;
  }
}

Thanks

@knoobie
Copy link
Contributor

knoobie commented Apr 7, 2022

@retomerz it's tagged with Good First Issue, so if you wanna contribute it, got ahead - it could probably be included in the upcoming release in June/July

@MarcinVaadin MarcinVaadin self-assigned this Jul 22, 2022
MarcinVaadin added a commit that referenced this issue Jul 22, 2022
Introduce getListeners(eventType) method on com.vaadin.flow.component.ComponentEventBus and shorthand method on com.vaadin.flow.component.Component

fixes: #9766
MarcinVaadin added a commit that referenced this issue Jul 22, 2022
Introduce getListeners(eventType) method on com.vaadin.flow.component.ComponentEventBus and shorthand method on com.vaadin.flow.component.Component

fixes: #9766
@MarcinVaadin
Copy link
Member

I've added support for getListeners, but as for your second issue - getting KeyCode from listener - even if now we have access to listeners the key property is not accessible. I could add just a getter for it but I don't see wider usecase for it.

As a workaround you can do sth like:

public class MyTextField extends TextField {

    class MyKeyEventListener<E extends KeyboardEvent> extends KeyEventListener<E> {
        private Key key;
        public MyKeyEventListener(ComponentEventListener<E> listener, Key key, KeyModifier... modifiers) {
            super(listener, key, modifiers);
            this.key = key;
        }

        public Key getKey() {
            return key;
        }
    }

    public MyTextField() {
        addKeyPressListener(new MyKeyEventListener<>(ev -> {}, Key.KEY_Y));
    }

    public boolean hasShortcutListener(Key key) {
        return getListeners(KeyboardEvent.class).stream()
                .filter(MyKeyEventListener.class::isInstance)
                .map(MyKeyEventListener.class::cast)
                .map(MyKeyEventListener::getKey)
                .anyMatch(key::equals);
    }

}

@MarcinVaadin MarcinVaadin moved this from Ready for Community to Work In Progress in Vaadin Flow enhancements backlog (Vaadin 10+) Jul 22, 2022
mshabarov pushed a commit that referenced this issue Aug 1, 2022
…ponent.ComponentEventBus (#14209)

Introduced com.vaadin.flow.component.ComponentEventBus.getListeners(Class<? extends ComponentEvent>) together with corresponding shorthand method (on Component) and static helper (on ComponentUtil).

Fixes #9766
Vaadin Flow enhancements backlog (Vaadin 10+) automation moved this from Work In Progress to Done / Pending Release Aug 1, 2022
@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 23.2.0.beta1 and is also targeting the upcoming stable 23.2.0 version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment