Phoenix Form · Single
<.form for={@form} phx-submit="save_phoenix"
class="flex flex-col gap-space-lg w-full max-w-xl"
>
<.date_picker
field={@form[:date]}
translation={%Corex.DatePicker.Translation{open_calendar: "Select date", close_calendar: "Select date", input: "Select date"}}
class="date-picker"
>
<:label>Date</:label>
<:trigger>
<.heroicon name="hero-calendar" class="icon" />
</:trigger>
<:prev_trigger>
<.heroicon name="hero-chevron-left" class="icon" />
</:prev_trigger>
<:next_trigger>
<.heroicon name="hero-chevron-right" class="icon" />
</:next_trigger>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.date_picker>
<.action type="submit" class="button ui-accent">Submit</.action>
</.form>
defmodule MyAppWeb.DatePickerFormLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
phoenix_form =
Phoenix.Component.to_form(%{"date" => ""}, as: :date_picker_phoenix, id: "date-picker-live-form-phoenix")
{:ok, assign(socket, :phoenix_form, phoenix_form)}
end
def handle_event("save_phoenix", %{"date_picker_phoenix" => params}, socket) do
date = params["date"] || ""
{:noreply,
assign(
socket,
:phoenix_form,
Phoenix.Component.to_form(%{"date" => date}, as: :date_picker_phoenix, id: "date-picker-live-form-phoenix")
)}
end
end
Phoenix Form · Multiple
<.form for={@form} phx-submit="save_phoenix_multiple"
class="flex flex-col gap-space-lg w-full max-w-xl"
>
<.date_picker
field={@form[:dates]}
selection_mode="multiple"
max_selected_dates={3}
translation={%Corex.DatePicker.Translation{open_calendar: "Select dates", close_calendar: "Select dates", input: "Dates"}}
class="date-picker"
>
<:label>Dates</:label>
<:trigger><.heroicon name="hero-calendar" class="icon" /></:trigger>
<:prev_trigger><.heroicon name="hero-chevron-left" class="icon" /></:prev_trigger>
<:next_trigger><.heroicon name="hero-chevron-right" class="icon" /></:next_trigger>
</.date_picker>
<.action type="submit" class="button ui-accent">Submit</.action>
</.form>
defmodule MyAppWeb.DatePickerFormLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
phoenix_multiple_form =
Phoenix.Component.to_form(%{"dates" => []},
as: :date_picker_phoenix_multiple,
id: "date-picker-live-form-phoenix-multiple"
)
{:ok, assign(socket, :phoenix_multiple_form, phoenix_multiple_form)}
end
def handle_event("save_phoenix_multiple", %{"date_picker_phoenix_multiple" => params}, socket) do
dates = Map.get(params, "dates", [])
{:noreply,
assign(
socket,
:phoenix_multiple_form,
Phoenix.Component.to_form(%{"dates" => List.wrap(dates)},
as: :date_picker_phoenix_multiple,
id: "date-picker-live-form-phoenix-multiple"
)
)}
end
end
Phoenix Form · Range
<.form for={@form} phx-submit="save_phoenix_range"
class="flex flex-col gap-space-lg w-full max-w-xl"
>
<.date_picker
field={@form[:date_range]}
selection_mode="range"
translation={%Corex.DatePicker.Translation{open_calendar: "Select date range", close_calendar: "Select date range", input: "Date range", range_start: "From", range_end: "To"}}
class="date-picker"
>
<:label>Date range</:label>
<:trigger><.heroicon name="hero-calendar" class="icon" /></:trigger>
<:prev_trigger><.heroicon name="hero-chevron-left" class="icon" /></:prev_trigger>
<:next_trigger><.heroicon name="hero-chevron-right" class="icon" /></:next_trigger>
</.date_picker>
<.action type="submit" class="button ui-accent">Submit</.action>
</.form>
defmodule MyAppWeb.DatePickerFormLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
phoenix_range_form =
Phoenix.Component.to_form(%{"date_range" => []},
as: :date_picker_phoenix_range,
id: "date-picker-live-form-phoenix-range"
)
{:ok, assign(socket, :phoenix_range_form, phoenix_range_form)}
end
def handle_event("save_phoenix_range", %{"date_picker_phoenix_range" => params}, socket) do
date_range = List.wrap(Map.get(params, "date_range", []))
{:noreply,
assign(
socket,
:phoenix_range_form,
Phoenix.Component.to_form(%{"date_range" => date_range},
as: :date_picker_phoenix_range,
id: "date-picker-live-form-phoenix-range"
)
)}
end
end
Phoenix Form + Ecto · Single
<.form
for={@form}
phx-change="validate_validate"
phx-submit="save_validate"
class="flex flex-col gap-space-lg w-full max-w-xl"
>
<.date_picker
field={@form[:date]}
value={@date_display}
translation={%Corex.DatePicker.Translation{open_calendar: "Select date", close_calendar: "Select date", input: "Select date"}}
class="date-picker"
>
<:label>Date (required)</:label>
<:trigger><.heroicon name="hero-calendar" class="icon" /></:trigger>
<:prev_trigger><.heroicon name="hero-chevron-left" class="icon" /></:prev_trigger>
<:next_trigger><.heroicon name="hero-chevron-right" class="icon" /></:next_trigger>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.date_picker>
<.action type="submit" class="button ui-accent">Submit</.action>
</.form>
defmodule MyAppWeb.DatePickerFormLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
validate_form =
%MyApp.Form.DatePickerForm{}
|> MyApp.Form.DatePickerForm.changeset_validate(%{})
|> Phoenix.Component.to_form(as: :date_picker_validate, id: "date-picker-validate-form-live")
{:ok, assign(socket, :validate_form, validate_form)}
end
def handle_event("validate_validate", %{"date_picker_validate" => params}, socket) do
changeset =
%MyApp.Form.DatePickerForm{}
|> MyApp.Form.DatePickerForm.changeset_validate(params)
|> Map.put(:action, :validate)
{:noreply,
assign(
socket,
:validate_form,
Phoenix.Component.to_form(changeset,
action: :validate,
as: :date_picker_validate,
id: "date-picker-validate-form-live"
)
)}
end
def handle_event("save_validate", %{"date_picker_validate" => params}, socket) do
case MyApp.Form.DatePickerForm.changeset_validate(%MyApp.Form.DatePickerForm{}, params) do
%Ecto.Changeset{valid?: true} = changeset ->
_data = Ecto.Changeset.apply_changes(changeset)
{:noreply,
assign(
socket,
:validate_form,
Phoenix.Component.to_form(
MyApp.Form.DatePickerForm.changeset_validate(%MyApp.Form.DatePickerForm{}, params),
as: :date_picker_validate,
id: "date-picker-validate-form-live"
)
)}
changeset ->
{:noreply,
assign(
socket,
:validate_form,
Phoenix.Component.to_form(changeset,
action: :insert,
as: :date_picker_validate,
id: "date-picker-validate-form-live"
)
)}
end
end
end
defmodule MyApp.Form.DatePickerForm do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :date, :date
field :dates, {:array, :date}
field :date_range, {:array, :date}
end
def changeset(form, attrs \\ %{}) do
form
|> cast(normalize_range_attrs(attrs), [:date, :dates, :date_range])
end
def changeset_validate(form, attrs \\ %{}) do
form
|> cast(attrs, [:date])
|> validate_required([:date], message: "can't be blank")
end
def changeset_validate_dates(form, attrs \\ %{}) do
form
|> cast(attrs, [:dates])
|> validate_required([:dates], message: "can't be blank")
|> validate_length(:dates, min: 1, message: "can't be blank")
end
def changeset_validate_range(form, attrs \\ %{}) do
form
|> cast(normalize_range_attrs(attrs), [:date_range])
|> validate_required([:date_range], message: "can't be blank")
|> validate_length(:date_range, min: 2, max: 2, message: "must be a start and end date")
end
defp normalize_range_attrs(%{"date_range" => range} = attrs) when is_binary(range) do
%{attrs | "date_range" => String.split(range, ",", trim: true)}
end
defp normalize_range_attrs(attrs), do: attrs
end
Phoenix Form + Ecto + Invalid
<.form for={@validate_invalid_form} id="date-picker-validate-form-live-invalid" phx-change="validate_invalid" phx-submit="save_invalid"
class="flex flex-col gap-space-lg w-full max-w-xl"
>
<.date_picker
id="date-picker-validate-live-invalid"
field={@validate_invalid_form[:date]}
selection_mode="single"
value={@validate_invalid_date_value}
invalid={Corex.FormField.invalid?(@validate_invalid_form[:date])}
class="date-picker"
>
<:label>Date *</:label>
<:trigger><.heroicon name="hero-calendar" class="icon" /></:trigger>
<:prev_trigger><.heroicon name="hero-chevron-left" class="icon" /></:prev_trigger>
<:next_trigger><.heroicon name="hero-chevron-right" class="icon" /></:next_trigger>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.date_picker>
<.action type="submit" id="date-picker-validate-form-live-invalid-submit" class="button ui-accent">Submit</.action>
</.form>
defmodule MyAppWeb.DatePickerFormLive do
use MyAppWeb, :live_view
def handle_event("validate_invalid", params, socket) do
nested = Map.get(params, "date_picker_validate_invalid", %{})
validate_ecto_invalid(socket, nested)
end
def handle_event("save_invalid", params, socket) do
nested = Map.get(params, "date_picker_validate_invalid", %{})
save_ecto_invalid(socket, nested)
end
end
defmodule MyApp.Form.DatePickerForm do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :date, :date
field :dates, {:array, :date}
field :date_range, {:array, :date}
end
def changeset(form, attrs \\ %{}) do
form
|> cast(normalize_range_attrs(attrs), [:date, :dates, :date_range])
end
def changeset_validate(form, attrs \\ %{}) do
form
|> cast(attrs, [:date])
|> validate_required([:date], message: "can't be blank")
end
def changeset_validate_dates(form, attrs \\ %{}) do
form
|> cast(attrs, [:dates])
|> validate_required([:dates], message: "can't be blank")
|> validate_length(:dates, min: 1, message: "can't be blank")
end
def changeset_validate_range(form, attrs \\ %{}) do
form
|> cast(normalize_range_attrs(attrs), [:date_range])
|> validate_required([:date_range], message: "can't be blank")
|> validate_length(:date_range, min: 2, max: 2, message: "must be a start and end date")
end
defp normalize_range_attrs(%{"date_range" => range} = attrs) when is_binary(range) do
%{attrs | "date_range" => String.split(range, ",", trim: true)}
end
defp normalize_range_attrs(attrs), do: attrs
end
Phoenix Form + Ecto · Multiple
<.form for={@form} phx-change="validate_dates" phx-submit="save_dates"
class="flex flex-col gap-space-lg w-full max-w-xl"
>
<.date_picker
field={@form[:dates]}
selection_mode="multiple"
max_selected_dates={3}
value={@date_display}
translation={%Corex.DatePicker.Translation{open_calendar: "Select dates", close_calendar: "Select dates", input: "Dates"}}
class="date-picker"
>
<:label>Dates (required)</:label>
<:trigger><.heroicon name="hero-calendar" class="icon" /></:trigger>
<:prev_trigger><.heroicon name="hero-chevron-left" class="icon" /></:prev_trigger>
<:next_trigger><.heroicon name="hero-chevron-right" class="icon" /></:next_trigger>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.date_picker>
<.action type="submit" class="button ui-accent">Submit</.action>
</.form>
defmodule MyAppWeb.DatePickerFormLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
validate_dates_form =
%MyApp.Form.DatePickerForm{}
|> MyApp.Form.DatePickerForm.changeset_validate_dates(%{})
|> Phoenix.Component.to_form(as: :date_picker_validate_dates, id: "date-picker-validate-dates-form-live")
{:ok, assign(socket, :validate_dates_form, validate_dates_form)}
end
def handle_event("validate_dates", %{"date_picker_validate_dates" => params}, socket) do
changeset =
%MyApp.Form.DatePickerForm{}
|> MyApp.Form.DatePickerForm.changeset_validate_dates(params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, :validate_dates_form, Phoenix.Component.to_form(changeset, action: :validate, as: :date_picker_validate_dates, id: "date-picker-validate-dates-form-live"))}
end
def handle_event("save_dates", %{"date_picker_validate_dates" => params}, socket) do
case MyApp.Form.DatePickerForm.changeset_validate_dates(%MyApp.Form.DatePickerForm{}, params) do
%Ecto.Changeset{valid?: true} = changeset ->
{:noreply,
assign(
socket,
:validate_dates_form,
Phoenix.Component.to_form(
MyApp.Form.DatePickerForm.changeset_validate_dates(%MyApp.Form.DatePickerForm{}, params),
as: :date_picker_validate_dates,
id: "date-picker-validate-dates-form-live"
)
)}
changeset ->
{:noreply,
assign(
socket,
:validate_dates_form,
Phoenix.Component.to_form(changeset,
action: :insert,
as: :date_picker_validate_dates,
id: "date-picker-validate-dates-form-live"
)
)}
end
end
end
Phoenix Form + Ecto · Range
<.form for={@form} phx-change="validate_range" phx-submit="save_range"
class="flex flex-col gap-space-lg w-full max-w-xl"
>
<.date_picker
field={@form[:date_range]}
selection_mode="range"
value={@date_display}
translation={%Corex.DatePicker.Translation{open_calendar: "Select date range", close_calendar: "Select date range", input: "Date range", range_start: "From", range_end: "To"}}
class="date-picker"
>
<:label>Date range (required)</:label>
<:trigger><.heroicon name="hero-calendar" class="icon" /></:trigger>
<:prev_trigger><.heroicon name="hero-chevron-left" class="icon" /></:prev_trigger>
<:next_trigger><.heroicon name="hero-chevron-right" class="icon" /></:next_trigger>
<:error :let={msg}>
<.heroicon name="hero-exclamation-circle" class="icon" />
{msg}
</:error>
</.date_picker>
<.action type="submit" class="button ui-accent">Submit</.action>
</.form>
defmodule MyAppWeb.DatePickerFormLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
validate_range_form =
%MyApp.Form.DatePickerForm{}
|> MyApp.Form.DatePickerForm.changeset_validate_range(%{})
|> Phoenix.Component.to_form(as: :date_picker_validate_range, id: "date-picker-validate-range-form-live")
{:ok, assign(socket, :validate_range_form, validate_range_form)}
end
def handle_event("validate_range", %{"date_picker_validate_range" => params}, socket) do
changeset =
%MyApp.Form.DatePickerForm{}
|> MyApp.Form.DatePickerForm.changeset_validate_range(params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, :validate_range_form, Phoenix.Component.to_form(changeset, action: :validate, as: :date_picker_validate_range, id: "date-picker-validate-range-form-live"))}
end
def handle_event("save_range", %{"date_picker_validate_range" => params}, socket) do
case MyApp.Form.DatePickerForm.changeset_validate_range(%MyApp.Form.DatePickerForm{}, params) do
%Ecto.Changeset{valid?: true} = changeset ->
{:noreply,
assign(
socket,
:validate_range_form,
Phoenix.Component.to_form(
MyApp.Form.DatePickerForm.changeset_validate_range(%MyApp.Form.DatePickerForm{}, params),
as: :date_picker_validate_range,
id: "date-picker-validate-range-form-live"
)
)}
changeset ->
{:noreply,
assign(
socket,
:validate_range_form,
Phoenix.Component.to_form(changeset,
action: :insert,
as: :date_picker_validate_range,
id: "date-picker-validate-range-form-live"
)
)}
end
end
end