Building a share button with Phoenix
Phoenix.Component is the Phoenix way of creating reusable function components in your frontend code. It has always worked just fine, but since the introduction of CollocatedHook, it’s never being easier to bring JavaScript into your components.
In this post, I will build a share button to show how they can work together in a LiveView.
The JavaScript piece
Web Share API allows sites to share text, links, and other contents to user-selected share targets, using the same mechanism as the underlying operating system.
It requires a secure context (HTTPS) to work, but apart from that, it’s a simple API. The only catch is that it requires a user-action, like a button click, to share content.
There are two methods in the API: navigator.canShare() and navigator.share(). The first validates whether the content is shareable, and the second sends it. Let’s see an example:
const content = {
title: "santoscodes",
text: "Building a share button with Phoenix",
url: "https://santoscodes.com"
};
const button = document.querySelector("button");
button.addEventListener("click", async () => {
try {
if (navigator.canShare(content)) {
await navigator.share(content);
}
} catch (err) {
console.error(err);
}
});
The Phoenix component
You can add a new function to core_components.ex, or create a new module to handle custom components; it’s your call. Here’s the code:
def share_button(assigns) do
~H"""
<button type="button" value="Hey, take a look at this!">
Share content
</button>
"""
end
The mix
CollocatedHook extracts JavaScript hooks from <script> tags defined (co-located) in places like a LiveView or in a Phoenix component. This makes the integration of JavaScript code with your components easier because everything can be defined in the same place. However, we must follow two requirements to use it:
- Phoenix version must be 1.8+;
- Your component must have an id.
Let’s finally mix everything together:
def share_button(assigns) do
~H"""
<button type="button" value="Hey, take a look at this!" id="share-button" phx-hook=".ShareButton">
Share content
</button>
<script :type={Phoenix.LiveView.ColocatedHook} name=".ShareButton">
export default {
mounted() {
const content = {
title: "santoscodes",
text: this.el.value,
url: window.location.href
};
this.el.addEventListener("click", async () => {
try {
if (navigator.canShare(content)) {
await navigator.share(content);
}
} catch (err) {
console.error(err);
}
});
}
</script>
"""
end
Perfect! The component definition and JavaScript integration are both defined in the same place, as they should be. Now, you can use it anywhere in your LiveViews, and it will work.