You can use the generic HTTP provider for any app - without the need to rely on WebIO.

# You can just create your own display functionfunction Base.display(d::MyWebDisplay, m::MIME"application/webio", app)
println(d.io, "outer html")
# calling show will make sure a server is running and serves dependencies# from AssetRegistry and a websocket connection gets established.show(d.io, m, app) #<- prints the html + scripts webio needs to work into ioprintln(d.io, "close outer html")
end# You can customize the server via the following environment variables:
ENV["JULIA_WEBIO_BASEURL"] ="url/to/base/route"# e.g. if you have a proxy
url = ENV["WEBIO_SERVER_HOST_URL"] ="127.0.0.1"# the url you want the server to listen on
http_port = ENV["WEBIO_HTTP_PORT"] ="8081"# the port you want the server to listen on# the url that the websocket connects to:
ENV["WEBIO_WEBSOCKT_URL"] =string(url, ":", http_port, "/webio_websocket/")

WebIO.render

WebIO exports WebIO.render generic function which can be extended to define how to render something into WebIO's DOM. Think of it as a better version of show(io::IO, m::MIME"text/html", x). Whenever an object is used as an argument to node, this render function will be called to create the Node object to display.

Note that @js just translates a Julia expression to the equivalent JavaScript, it does not compile the code. The variables and functions you reference in a @js expression must be defined in the JavaScript context it will run in (and need not be defined in Julia).

Loading JavaScript dependencies

You can load dependencies by creating a Scope object and passing in imports argument.

Communicating between Julia and JavaScript

w =Scope()

A scope object acts as a container for communication (more details below). To exchange values between JavaScript and Julia, we also need to add Observable objects to the scope. This can be done by passing the scope, and an identifier for the observable (as string) and a default value to the Observable constructor:

obs =Observable(w, "rand-value", 0.0)

You can get the value of obs with the syntax obs[]. You can set the value using the syntax obs[] = val. To listen to changes to the value you can use the on function.

on(f, obs)

This will run f on every update to obs.

Sending values from JavaScript to Julia

Below is a scope which communicates with Julia. Let's run through its construction line-by-line. The following scope contains a button which sends a random number, generated in JavaScript, to Julia. We will print this number on the Julia side.

w is a Scope object, it acts a scope or context for communication. Every call to random_print_button will create a new scope and hence keep the updates contained within it. This allows there to be many instances of the same scope on a page.

An Observable is a value that can change over time. Observable(w, "rand-value", 0.0) creates an observable by the name "rand-value" associated with scope w. on(f, x) sets up an event handler such that f is called with the value of x every time x is updated.

An observable can be updated using the x[] = value syntax on Julia. To update the observable from the JavaScript side, you can use the following syntax:

@js$obs[] = Math.random()

This will return a JSString which you can use anywhere WebIO expects JavaScript, such as a event handler. But an event handler should be a function so you would need to enclose this in a function: @js () -> $obs[] = Math.random().

creates a button UI which updates the obs observable with Math.random() (executed in JS) on every click.

Notice the last expression actually calls the scope w with the contents to display. This causes the contents to be wrapped in w's context. All uses of observables associated with w (e.g. obs) should be enclosed in the scope w.

Sending values from Julia to JavaScript

Here's a clock where the time is formatted and updated every second from Julia. We use the onjs handler and mutate the #clock DOM element to acheive this.

The javascript function passed to onjs gets the value of the update as the argument. this is set to the Scope object. Notice the use of this.dom.querySelector("#clock"). this.dom contains the rendered DOM of the scope. querySelector("#<id>") will look up the element which has the id <id>. clock.textContent = val will set the text contained in clock, the DOM element.

For an even easier way to send values from Julia to JavaScript, we can simply rely on the fact that WebIO knows how to render Observables directly to HTML. In this case WebIO will automatically construct a Scope and insert the relevant JavaScript to update the rendered content whenever the Observable changes value: