Phoenix.LiveView.ColocatedHook (Phoenix LiveView v1.1.0-rc.2)
View SourceA special HEEx :type
that extracts hooks
from a co-located <script>
tag at compile time.
Introduction
Colocated hooks are defined as with :type={Phoenix.LiveView.ColocatedHook}
:
defmodule MyAppWeb.DemoLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
{:ok, socket}
end
def render(assigns) do
~H"""
<input type="text" name="user[phone_number]" id="user-phone-number" phx-hook=".PhoneNumber" />
<script :type={Phoenix.LiveView.ColocatedHook} name=".PhoneNumber">
export default {
mounted() {
this.el.addEventListener("input", e => {
let match = this.el.value.replace(/\D/g, "").match(/^(\d{3})(\d{3})(\d{4})$/)
if(match) {
this.el.value = `${match[1]}-${match[2]}-${match[3]}`
}
})
}
}
</script>
"""
end
end
You can read more about the internals of colocated hooks in the documentation for colocated JS.
A brief summary: at compile time, the hook's code is extracted into a special folder, typically in your _build
directory.
Each hook is also import
ed into a special manifest file. The manifest file provides
a named export
which allows it to be imported by any JavaScript bundler that supports ES modules:
import {hooks} from "phoenix-colocated/my_app"
console.log(hooks);
/*
{
"MyAppWeb.DemoLive.PhoneNumber": {...},
...
}
*/
Options
Colocated hooks are configured through the attributes of the <script>
tag.
The supported attributes are:
name
- The name of the hook. This is required and must start with a dot, for example:name=".myhook"
. The same name must be used when referring to this hook in thephx-hook
attribute of another HTML element.runtime
- If present, the hook is not extracted, but instead registered at runtime. You should only use this option if you know that you need it. It comes with some limitations:- The content is not processed by any bundler, therefore it must only use features supported by the targeted browsers.
- You need to take special care about any Content Security Policies that may be in place. See the section on runtime hooks below for more details.
Runtime hooks
Runtime hooks are a special kind of colocated hook that are not removed from the DOM when rendering the component. Instead, the hook's code is executed directly in the browser with no bundler involved.
One example where this can be useful is when you are creating a custom page for a library
like Phoenix.LiveDashboard
. The live dashboard already bundles its hooks, therefore there
is no way to add new hooks to the bundle when the live dashboard is used inside your application.
Because of this, runtime hooks must also use a slightly different syntax. While in normal
colocated hooks you'd write an export default
statement, runtime hooks must evaluate to the
hook itself:
<script :type={Phoenix.LiveView.ColocatedHook} name=".MyHook" runtime>
{
mounted() {
...
}
}
</script>
This is because the hook's code is wrapped by LiveView into something like this:
window["phx_hook_HASH"] = function() {
return {
mounted() {
...
}
}
}
Still, even for runtime hooks, the hook's name needs to start with a dot and is automatically prefixed with the module name to avoid conflicts with other hooks.
When using runtime hooks, it is important to think about any limitations that content security policies may impose. If CSP is involved, the only way to use runtime hooks is by using CSP nonces:
<script :type={Phoenix.LiveView.ColocatedHook} name=".MyHook" runtime nonce={@script_csp_nonce}>
function() {
return ...;
}
</script>
This is assuming that the @script_csp_nonce
assign contains the nonce value that is also
sent in the Content-Security-Policy
header.