Skip to content

Instantly share code, notes, and snippets.

@mcrumm
Last active August 20, 2024 13:42
Show Gist options
  • Save mcrumm/88313d9f210ea17a640e673ff0d0232b to your computer and use it in GitHub Desktop.
Save mcrumm/88313d9f210ea17a640e673ff0d0232b to your computer and use it in GitHub Desktop.
flatpickr + LiveView example
// assets/js/app.js
// ...
import Pickr from "./pickr"
const hooks = {
Pickr
}
// ...
let liveSocket = new LiveSocket("/live", Socket, { hooks, params: { _csrf_token: csrfToken } })
// assets/js/pickr/index.js
import flatpickr from "flatpickr";
const Pickr = {
mounted() {
this.pickr = flatpickr(this.el, {
wrap: true,
altInput: this.el.dataset.pickrAltFormat ? true : false,
altFormat: this.el.dataset.pickrAltFormat || "d M Y",
dateFormat: this.el.dataset.pickrDateFormat || "Y-m-d"
})
},
updated() {
const altFormat = this.el.dataset.pickrAltFormat
const wasFormat = this.pickr.config.altFormat
if (altFormat !== wasFormat) {
this.pickr.destroy()
this.pickr = flatpickr(this.el, {
wrap: true,
altInput: this.el.dataset.pickrAltFormat ? true : false,
altFormat: this.el.dataset.pickrAltFormat || "d M Y",
dateFormat: this.el.dataset.pickrDateFormat || "Y-m-d"
})
}
},
destroyed() {
this.pickr.destroy()
}
}
export default Pickr
# lib/my_app_web/live/pickr_live.ex
defmodule MyAppWeb.PickrLive do
use MyAppWeb, :live_view
@impl Phoenix.LiveView
def render(assigns) do
~L"""
<h2>flatpickr example</h2>
<form id="pickr-form" action="#" phx-change="validate">
<div id="starts-at-pickr" class="flatpickr" phx-update="ignore" phx-hook="Pickr" data-pickr-alt-format="<%= @format %>">
<label for="starts-at">Starts at</label>
<input type="text" id="starts-at" name="pickr[starts_at]" placeholder="Select Start Date.." data-input />
</div>
<div id="ends-at-pickr" class="flatpickr" phx-update="ignore" phx-hook="Pickr" data-pickr-alt-format="<%= @format %>">
<label for="ends-at">Ends at</label>
<input type="text" id="ends-at" name="pickr[ends_at]" placeholder="Select End Date.." data-input />
</div>
</form>
<%= if @values do %>
<p>Values: <code><%= @values %></code></p>
<% end %>
<hr />
<form id="format" action="#" phx-change="format">
<label>Alt Format</label>
<input type="text" name="format" value="<%= @format %>"/>
</form>
"""
end
@impl Phoenix.LiveView
def handle_event("validate", %{"pickr" => _} = values, socket) do
{:noreply, assign(socket, :values, inspect(values, pretty: true))}
end
def handle_event("format", %{"format" => format}, socket) do
{:noreply, assign(socket, :format, format)}
end
@impl Phoenix.LiveView
def mount(_, _, socket) do
{:ok, socket |> assign(:values, nil) |> assign(:format, "")}
end
end
@mrcampbell
Copy link

This is what I needed! It's funny because I saw your ElixirConf talk on Phoenix the other day, and I should have known my google search would have led to you one way or another 😂

@micahsoftdotexe
Copy link

Greetings, I am new to Phoenix and LiveView. I am trying to implement this but the issues I am having pertain to the css. It appears as though the css renders the datepicker internals at the bottom of the page constantly and does not wait to be clicked on the datepicker input. Am I doing something wrong or do I need to do some more work for modern versions of liveview?

@absowoot
Copy link

@micahsoftdotexe I had a similar issue and the updates below resolved it for me:

<div id="date-pickr" phx-update="ignore">
    <div id="starts-at-pickr" class="flatpickr" phx-hook="Pickr" data-pickr-alt-format="<%= @format %>">
      <label for="starts-at">Starts at</label>
      <input type="text" id="starts-at" name="pickr[starts_at]" placeholder="Select Start Date.." data-input />
    </div>
</div>
const Pickr = {
    mounted() {
        this.pickr = flatpickr(this.el, {
            wrap: true,
            appendTo: this.el.querySelector("input"),
            static: true,
            altInput: this.el.dataset.pickrAltFormat ? true : false,
            altFormat: this.el.dataset.pickrAltFormat || "d M Y",
            dateFormat: this.el.dataset.pickrDateFormat || "Y-m-d",
        });
    },
    updated() {
        const altFormat = this.el.dataset.pickrAltFormat;
        const wasFormat = this.pickr.config.altFormat;
        if (altFormat !== wasFormat) {
            this.pickr.destroy();
            this.pickr = flatpickr(this.el, {
                wrap: true,
                appendTo: this.el.querySelector("input"),
                static: true,
                altInput: this.el.dataset.pickrAltFormat ? true : false,
                altFormat: this.el.dataset.pickrAltFormat || "d M Y",
                dateFormat: this.el.dataset.pickrDateFormat || "Y-m-d",
            });
        }
    },
    destroyed() {
        this.pickr.destroy();
    },
}

@micahsoftdotexe
Copy link

@absowoot Thank you for the help. Unfortunately I am still having issues with the css with the arrows being so large and it still being present even when the input has not been clicked. This time it isn't loading at the bottom of the page but in the right spot Could I have imported it wrong or could I have done something else wrong?

@absowoot
Copy link

@micahsoftdotexe Try adding a relative class to the div where phx-hook is located. Also, double-check that the tailwind classes are being applied IE the arrow SVG should have a width of 1rem with the w-4 class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment