Make it possible for executeJavaScript snippets to reference values exported by a JS module
See original GitHub issueWhen I write JavaScript into a JS module file, I can easily use stuff exported from another module:
import { bar } from "foo";
bar("Hello");
Things get messy if I want to pass a value from the server to bar
. Without any framework support, I’d have to create a separate myModule.js
module file that imports bar
and publishes it somewhere in the global namespace.
import { bar } from "foo";
window.publishedBar = bar;
My Java logic would then need to have @JsModule("./myModule.js")
and only then could I do executeJavaScript("publishedBar($0)", "Hello");
. Even though this would work, it would require quite much boilerplate.
My assumption is that information about what to import from where would have to be included in the input to webpack (i.e. main.js
), which in turn means that it would have to be expressed in a declarative way in the Java code so that the logic that generates main.js
could find it.
Since generation of main.js
is already based on scanning for @JsModue
annotations, we could use additional information in those annotations to generate code in main.js
that imports specific parts and stores them somewhere for future use. Based on something like @JsModule(value="foo", imports = {"bar", "baz"})
, we could generate main.js
like this:
import {bar as generated123, baz as generated321} from "foo";
window.Vaadin.Flow.imports["foo"] = {bar: generated123, baz: generated321};
Based on this, I could do executeJavaScript("Vaadin.Flow.imports['foo'].bar($0)", "Hello")
. The Vaadin.Flow.imports
syntax is still quite verbose and not very discoverable, but I would at the very least not have to create a separate myModule.js
file. In this case, I could have written .foo
instead of ['foo']
, but that wouldn’t be feasible if importing from e.g. @vaadin/vaadin-grid/someFile.js
.
We could also consider always injecting an imports
variable into the scope that we set up when evaluating JS expressions from the server, thus simplifying the expression to executeJavaScript("imports['foo'].bar($0), "Hello");
.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:8
- Comments:7 (7 by maintainers)
Top GitHub Comments
Based on my latest thinking, we should not not have
executeJs
at all and thus also not have any problem with referencing JS module exports in such snippets. The reason for that no havingexecuteJs
at all is that it’s unfriendly to CSP and has a possibility of causing XSS vulnerabilities if used in the wrong way.Instead, I’m proposing a new mechanism in https://github.com/vaadin/flow/issues/10759 which would as an added benefit also give natural interop with JS modules.
The original example in this issue with passing a string from the server to a
bar
function exported from afoo
module would then be expressed by defining a Java interface as an overlay over that module, acquiring a proxy instance of that interface from the framework and invoking a method on that proxy.Interface definition:
Usage:
To make more advanced integrations e.g. based on combining multiple modules, one can create an intermediate module with appropriate imports that would then export the combining logic. This intermediate module works in exactly the same way as any other JS module which means that it’s free to make arbitrary imports and exports.
I used a workaround JS like file:
This works but is rather inconvenient.