With the exception of CustomEventInit not being generic, this code works,
thanks Thomas.
In an example of event to signal that particular user is saved, this is what
I got working:
Declarations:
public static final String SavedEventType = "saved";
Init:
addEventListener(SavedEventType, this::onSaved);
Dispatch:
public void save(User user) {
CustomEventInit eventInit = CustomEventInit.create();
eventInit.setDetail(user);
eventInit.setBubbles(false);
CustomEvent event = new CustomEvent(SavedEventType, eventInit);
dispatchEvent(event);
}
Listener:
public void onSaved(Event event) {
CustomEvent customEvent = (CustomEvent) event;
User user = (User) customEvent.detail;
Log.info("User is " + user.getId());
}
Looks very fragile, especially if I decide to use complex details in which
case I'll have to expose internal structure of that field.
IMHO, best solution would be to extend CustomEvent, but I didn't find a way
to do so (as least not in a way to be able to *receive* instances of that
descended class, not even using Js.uncheckedCast, which, to my understanding
of JsInterop, is not even possible), so this is best I got:
public class SavedEvent {
public static final String Type = SavedEvent.class.getName();
@JsType(isNative = true, name = "CustomEventInit", namespace =
JsPackage.GLOBAL)
public static interface Init extends CustomEventInit {
@JsOverlay
static SavedEvent.Init create() {
SavedEvent.Init init = Js.uncheckedCast(CustomEventInit.create());
init.setBubbles(false);
return init;
}
@JsOverlay
default User getUser() {
return (User) getDetail();
}
@JsOverlay
default SavedEvent.Init setUser(User user) {
setDetail(user);
return this;
}
@JsOverlay
default CustomEvent event() {
return new CustomEvent(SavedEvent.Type, this);
}
}
public interface Listener extends EventListener {
default void handleEvent(Event event) {
handleEvent(new SavedEvent(event));
}
void handleEvent(SavedEvent event);
}
public SavedEvent(Event nativeEvent) {
// add at least some safety...
if (!(nativeEvent instanceof CustomEvent) || !Type.equals(nativeEvent.type))
throw new IllegalArgumentException();
this.nativeEvent = (CustomEvent) nativeEvent;
}
public CustomEvent getNativeEvent() {
return nativeEvent;
}
public User getUser() {
return (User) nativeEvent.detail;
}
private final CustomEvent nativeEvent;
}
Which contains a lot of boilerplate wrappers, but at least now dispatchers
and listeners (which are exposed to users) looks more decent:
addEventListener(SavedEvent.Type, this::onSaved);
public void save(User user) {
dispatchEvent(SavedEvent.Init.create().setUser(user).event());
}
public void onSaved(SavedEvent event) {
Log.info("Saved user is " + event.getUser().getId());
}
Any suggestions for further simplifications when working with specialized
events?
-gkresic.