-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
prototyping with other client-server-comm improvements that could be …
…useful for Flow (add-ons)
- Loading branch information
Showing
2 changed files
with
120 additions
and
0 deletions.
There are no files selected for viewing
69 changes: 69 additions & 0 deletions
69
src/main/java/org/vaadin/addons/velocitycomponent/VElement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package org.vaadin.addons.velocitycomponent; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.vaadin.flow.dom.Element; | ||
import com.vaadin.flow.function.SerializableConsumer; | ||
import com.vaadin.flow.shared.Registration; | ||
import elemental.json.JsonType; | ||
import elemental.json.JsonValue; | ||
import org.apache.commons.lang3.StringUtils; | ||
|
||
import static java.util.Arrays.stream; | ||
|
||
public class VElement { | ||
|
||
private static ObjectMapper objectMapper = new ObjectMapper(); | ||
|
||
private final Element element; | ||
|
||
private VElement(Element element) { | ||
this.element = element; | ||
} | ||
|
||
public static VElement of(Element element) { | ||
return new VElement(element); | ||
} | ||
|
||
/** | ||
* Listen to an event of a specific type. The event type is determined by the class of the event, | ||
* the name can contain optional "Event" postfix which is ignored in DOM event name. | ||
* The event payload is expected to be a JSON object or a string and serialized to the event type | ||
* using Jackson ObjectMapper. | ||
* | ||
* On the client side, the event should be dispatched with a CustomEvent with the detail property. | ||
* | ||
* @param eventType the class of the event | ||
* @param listener the listener to be called when the event is fired | ||
* @return a registration that can be used to remove the listener | ||
* @param <T> the type of the event | ||
*/ | ||
public <T> Registration on(Class<T> eventType, SerializableConsumer<T> listener) { | ||
String simpleName = eventType.getSimpleName(); | ||
if(simpleName.endsWith("Event")) { | ||
simpleName = simpleName.substring(0, simpleName.length() - 5); | ||
} | ||
String kebabCased = stream(StringUtils.splitByCharacterTypeCamelCase(simpleName)) | ||
.map(s -> s.toLowerCase()) | ||
.reduce((a, b) -> a + "-" + b).get(); | ||
|
||
return element.addEventListener(kebabCased, event -> { | ||
JsonValue jsonValue = event.getEventData().get("event.detail"); | ||
T value; | ||
if(jsonValue.getType() == JsonType.OBJECT) { | ||
try { | ||
value = objectMapper.readValue(jsonValue.toJson(), eventType); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} else { | ||
try { | ||
value = objectMapper.readValue(jsonValue.asString().toString(), eventType); | ||
} catch (JsonProcessingException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
listener.accept(value); | ||
}).addEventData("event.detail"); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/test/java/org/vaadin/addons/usageexample/VElementView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package org.vaadin.addons.usageexample; | ||
|
||
import com.vaadin.flow.component.button.Button; | ||
import com.vaadin.flow.component.html.H1; | ||
import com.vaadin.flow.component.html.Paragraph; | ||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; | ||
import com.vaadin.flow.router.Route; | ||
import org.vaadin.addons.velocitycomponent.VElement; | ||
|
||
@Route | ||
public class VElementView extends VerticalLayout { | ||
|
||
public record FooBar(String foo, String bar, String car) {} | ||
|
||
public VElementView() { | ||
add(new H1("Example to listen DOM events easily!")); | ||
|
||
VElement.of(getElement()).on(FooBar.class, event -> { | ||
String bar = event.bar(); | ||
add(new Paragraph("Received foo-bar event with detail: " + event)); | ||
}); | ||
|
||
add(new Button("Click me", e -> { | ||
getElement().executeJs(""" | ||
this.dispatchEvent(new CustomEvent('foo-bar', { | ||
detail: { | ||
foo: 'foo', | ||
bar: 'bar', | ||
car: 'car' | ||
} | ||
})); | ||
"""); | ||
})); | ||
|
||
add(new Button("Click me too", e -> { | ||
// This is in theory bit more efficient for the server | ||
// as it does not need to deserialize-serialize (with GWT library) | ||
// and then desirialize with Jackson, but only once with Jackson | ||
getElement().executeJs(""" | ||
this.dispatchEvent(new CustomEvent('foo-bar', { | ||
detail: JSON.stringify({ | ||
foo: 'foo', | ||
bar: 'bar', | ||
car: 'car' | ||
}) | ||
})); | ||
"""); | ||
})); | ||
|
||
} | ||
} |