Last active
August 28, 2020 15:57
-
-
Save Legioth/6fde19fe22b3135a9214ecdb055296c7 to your computer and use it in GitHub Desktop.
JavaScript Renderer for Flow
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
package org.vaadin.leif; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.Optional; | |
import org.slf4j.LoggerFactory; | |
import com.vaadin.flow.data.provider.CompositeDataGenerator; | |
import com.vaadin.flow.data.provider.DataGenerator; | |
import com.vaadin.flow.data.provider.DataKeyMapper; | |
import com.vaadin.flow.data.renderer.Renderer; | |
import com.vaadin.flow.data.renderer.RendererUtil; | |
import com.vaadin.flow.data.renderer.Rendering; | |
import com.vaadin.flow.dom.Element; | |
import com.vaadin.flow.function.SerializableBiConsumer; | |
import com.vaadin.flow.function.SerializableConsumer; | |
import com.vaadin.flow.function.ValueProvider; | |
import com.vaadin.flow.internal.JsonSerializer; | |
import com.vaadin.flow.internal.nodefeature.ReturnChannelMap; | |
import com.vaadin.flow.internal.nodefeature.ReturnChannelRegistration; | |
import elemental.json.Json; | |
import elemental.json.JsonArray; | |
import elemental.json.impl.JsonUtil; | |
public class JsRenderer<T> extends Renderer<T> { | |
private String expression; | |
private Map<String, SerializableBiConsumer<T, JsonArray>> clientCallables = new HashMap<>(); | |
JsRenderer(String expression) { | |
this.expression = expression; | |
} | |
@Override | |
public Rendering<T> render(Element container, DataKeyMapper<T> keyMapper, Element contentTemplate) { | |
throw new UnsupportedOperationException(); | |
} | |
@Override | |
public Rendering<T> render(Element container, DataKeyMapper<T> keyMapper) { | |
ReturnChannelRegistration returnChannel = container.getNode().getFeature(ReturnChannelMap.class) | |
.registerChannel(arguments -> { | |
String handlerName = arguments.getString(0); | |
String itemKey = arguments.getString(1); | |
JsonArray args; | |
if (arguments.length() == 3) { | |
args = arguments.getArray(2); | |
} else { | |
args = Json.createArray(); | |
} | |
SerializableBiConsumer<T, JsonArray> handler = clientCallables.get(handlerName); | |
if (handler == null) { | |
throw new IllegalStateException(handlerName); | |
} | |
T item = keyMapper.get(itemKey); | |
if (item != null) { | |
handler.accept(item, args); | |
} else { | |
LoggerFactory.getLogger(RendererUtil.class.getName()).info( | |
"Received an event for the handler '{}' with item key '{}', but the item is not present in the KeyMapper. Ignoring event.", | |
handlerName, itemKey); | |
} | |
}); | |
StringBuilder builder = new StringBuilder(); | |
builder.append("this.renderer = function(root, column, row) { var item = row.item;\n "); | |
// TODO Maybe defer this to beforeClientResponse so that we can include | |
// callables added after the renderer is set | |
clientCallables.keySet().forEach(handlerName -> { | |
builder.append("var ").append(handlerName).append(" = function() { $0(").append(JsonUtil.quote(handlerName)) | |
.append(", item.key, arguments[0] instanceof Event ? [] : Array.prototype.slice.call(arguments)) };\n"); | |
}); | |
builder.append(expression); | |
builder.append("\n}"); | |
container.executeJs(builder.toString(), returnChannel); | |
return new Rendering<T>() { | |
@Override | |
public Optional<DataGenerator<T>> getDataGenerator() { | |
Map<String, ValueProvider<T, ?>> valueProviders = getValueProviders(); | |
if (valueProviders == null || valueProviders.isEmpty()) { | |
return Optional.empty(); | |
} | |
CompositeDataGenerator<T> composite = new CompositeDataGenerator<>(); | |
valueProviders.forEach((key, provider) -> composite.addDataGenerator( | |
(item, jsonObject) -> jsonObject.put(key, JsonSerializer.toJson(provider.apply(item))))); | |
return Optional.of(composite); | |
} | |
@Override | |
public Element getTemplateElement() { | |
return null; | |
} | |
}; | |
} | |
public JsRenderer<T> withProperty(String property, ValueProvider<T, ?> provider) { | |
setProperty(property, provider); | |
return this; | |
} | |
public JsRenderer<T> withClientCallable(String functionName, SerializableConsumer<T> handler) { | |
return withClientCallable(functionName, (item, ignore) -> handler.accept(item)); | |
} | |
public JsRenderer<T> withClientCallable(String functionName, SerializableBiConsumer<T, JsonArray> handler) { | |
// TODO validate functionName | |
clientCallables.put(functionName, handler); | |
return this; | |
} | |
public static <T> JsRenderer<T> of(String expression) { | |
return new JsRenderer<>(expression); | |
} | |
} |
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
grid.addColumn(JsRenderer.<String> of("root.textContent = item.foo").withProperty("foo", item -> item)).setHeader("Test"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment