Skip to content

Instantly share code, notes, and snippets.

@doorgan
Created February 25, 2021 19:08
Show Gist options
  • Save doorgan/b05bff1772b9ed43a5fcf4b7fbab2f38 to your computer and use it in GitHub Desktop.
Save doorgan/b05bff1772b9ed43a5fcf4b7fbab2f38 to your computer and use it in GitHub Desktop.
LiveElement helper
// ...
import { define, LiveElement } from "./live_element";
define("foo", {
mounted() {
console.log("MOUNTED:", this.el);
},
});
let liveSocket = new LiveSocket("/live", Socket, {
params: { _csrf_token: csrfToken },
hooks: { LiveElement: LiveElement() },
});
// ...
<%= LiveElement.live_element("button", id: "foo", data_live_hooks: "foo", data_live_event_click: "live_element_click") do %>
Click me!
<% end %>
# ...
def handle_event("live_element_click", _, socket) do
IO.inspect("Click received from LiveElement")
{:noreply, socket}
end
# ...
defmodule LiveElement do
import Phoenix.HTML.Tag
def live_element(name, do: block) do
live_element(name, block, [])
end
def live_element(name, content) do
live_element(name, content, [])
end
def live_element(name, attrs, do: block) when is_list(attrs) do
live_element(name, block, attrs)
end
def live_element(name, content, attrs) when is_list(attrs) do
attrs = Keyword.merge(attrs, phx_hook: "LiveElement")
content_tag(name, content, attrs)
end
end
const defined = {};
const LIVE_HOOKS = "data-live-hooks";
const EVENT_PREFIX = "data-live-event";
export const define = (name, hook) => {
if (name in defined) return;
defined[name] = hook;
};
export const LiveElement = (options = {}) => {
const hook_attr = options.attribute || LIVE_HOOKS;
const event_prefix = options.event_prefix || EVENT_PREFIX;
return {
mounted() {
/** @type {HTMLElement} */
const el = this.el;
if (!el.hasAttribute(hook_attr)) {
console.warn("Live element is not using any hook.", el);
return;
}
// Load hooks
const hook_names = el.getAttribute(hook_attr).split(" ");
this.hooks = {};
for (let hook of hook_names) {
this.hooks[hook] = {
...defined[hook],
el: this.el,
viewName: this.viewName,
pushEvent: this.pushEvent,
pushEventTo: this.pushEventTo,
handleEvent: this.handleEvent,
};
this.hooks[hook].mounted();
}
// Handle events
const attributes = el.attributes;
const prefix_regex = new RegExp(`${event_prefix}-`);
for (let i = 0; i < attributes.length; i++) {
if (prefix_regex.test(attributes[i].name)) {
const event = attributes[i].name.replace(prefix_regex, "");
const liveview_event = attributes[i].value;
this.el.addEventListener(event, ({ detail }) => {
this.pushEvent(liveview_event, detail);
});
}
}
},
beforeUpdate() {
if (!this.hooks) return;
for (let hook of this.hooks) {
hook.beforeUpdate();
}
},
updated() {
if (!this.hooks) return;
for (let hook of this.hooks) {
hook.updated();
}
},
destroyed() {
if (!this.hooks) return;
for (let hook of this.hooks) {
hook.destroyed();
}
},
disconnected() {
if (!this.hooks) return;
for (let hook of this.hooks) {
hook.disconnected();
}
},
reconnected() {
if (!this.hooks) return;
for (let hook of this.hooks) {
hook.reconnected();
}
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment