This can be implemented in many, maaany… different ways. Some examples:
1. Message passing
In Elixir:
defmodule MyComponent do
use Hologram.Component
port :just_draw_it
def action(:my_action, _params, component) do
send_port_message(:just_draw_it, ["my_canvas", 100, 200, [{34, 223}, {13, 93}]])
component
end
end
In JS:
import * from Chart;
window.Hologram.ports.just_draw_it = (element_id, width, height, data_points) => {
const element = document.getElementById(element_id)
const chart = new Chart(element, width, height)
chart.draw(data_points)
}
Message passing is async, so we don’t expect a result from just_draw_it function.
Also, the port definitions and calls could as well look more like function calls.
2. Foreign Function Interface (FFI)
In Elixir:
defjs greet(name), "./my_js_file.mjs", "greet"
In JS:
export function greet(name) {
return `Hello, ${name}!`;
}
or we can go even further, by separating interop functionality to specialized interop modules like here:
defmodule MyJSInterop do
use Hologram.JS
js_dep :local, "./my_js_file.mjs", "greet"
js_dep, :npm, "some_lib", "my_fun"
defjs greet(name), “greet”
(…)
end
potentially even providing specialized functions to handle the Elixir/JS semantic mismatch:
def just_draw_it(element_id, width, height, data_points) do
element =
"document"
|> js_ref()
|> js_call("getElementById", [element_id])
if js_unwrap(element) == nil do
raise "Element not found"
end
chart =
"Chart"
|> js_ref()
|> js_create([element, %{width: width, height: height}])
chart
|> js_read("id")
|> IO.inspect(label: "Chart id")
js_call(chart, "draw", data_points)
js_call(chart, "addEventListener", [
"pointClick",
fn point -> IO.inspect(point, label: "Point clicked") end
])
chart
|> js_call("getWidth")
|> js_unwrap()
|> IO.inspect(label: "Chart width")
chart
|> js_call("getHeight")
|> js_unwrap()
|> IO.inspect(label: "Chart height")
end
However it could also potentially get out of hand, and requires JS knowledge anyway.
JS → Hologram could be similar for both cases, we could simply dispatch a Hologram action or command like this:
In JS:
Hologram.dispatchAction("MyComponent", "my_action", "my_target", params)
Calling Elixir modules wouldn’t make sense, because JS doesn’t know what Elixir modules have been transpiled, so such call should go through pages or components. Hologram sees modules used in pages/components and can include them in the bundle.