Skip to content

Instantly share code, notes, and snippets.

@jonneroelofs
Created December 21, 2021 13:04
Show Gist options
  • Save jonneroelofs/b4be067329cb12777c4fcf2658013bc2 to your computer and use it in GitHub Desktop.
Save jonneroelofs/b4be067329cb12777c4fcf2658013bc2 to your computer and use it in GitHub Desktop.
Flatpickr wrapped with Alpine.js in blade component for Laravel Livewire
<div x-data="datepicker(@entangle($attributes->wire('model')))" class="relative">
<div class="flex flex-col">
<label>Date</label>
<div class="flex items-center gap-2">
<input type="text" x-ref="myDatepicker" x-model="value">
<span class="cursor-pointer underline" x-on:click="reset">
<x-icon.x></x-icon.x>
</span>
</div>
</div>
</div>
@once
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('datepicker', (model) => ({
value: model,
init(){
this.pickr = flatpickr(this.$refs.myDatepicker, {})
this.$watch('value', function(newValue){
this.pickr.setDate(newValue);
}.bind(this));
},
reset(){
this.value = null;
}
}))
})
</script>
@endonce
<div class="bg-dark">
<div class="grid place-items-center min-h-screen">
<div class="shadow rounded p-4 border bg-white h-[278px] flex flex-col gap-4">
<h1 class="text-xl font-semibold text-gray-700">Event form</h1>
<div class="space-y-4">
<x-datepicker label="Starts at" wire:model="event.starts_at">
</x-datepicker>
<x-datepicker label="Ends at" wire:model="event.ends_at">
</x-datepicker>
</div>
<div>
<button
class="bg-success-500 text-white px-2 py-1 rounded"
wire:click="startToday()"
>
Start Today
</button>
</div>
</div>
</div>
</div>
<?php
namespace App\Http\Livewire;
use App\Models\Event;
use Livewire\Component;
class EventForm extends Component
{
public $event;
public function rules()
{
return [
'event.starts_at' => ['required'],
'event.ends_at' => ['required'],
];
}
public function mount()
{
$this->event = Event::make();
}
public function startToday()
{
$this->event->starts_at = today()->format('Y-m-d');
$this->event->ends_at = today()->format('Y-m-d');
}
public function render()
{
return view('livewire.event-form');
}
}
@slakbal
Copy link

slakbal commented Jul 19, 2022

@davisngl do you have an example?

@christoferd
Copy link

Incredible how complicated this is.
I have managed to get it to work in my project, but not if I add options to the flatpicker init.
For some reason it won't format the date and just shows a blank.
{dateFormat: 'Y-m-d', altFormat: 'Y-m-d', altInput: true, locale: 'es'}

@davisngl
Copy link

davisngl commented Jul 17, 2023

@davisngl do you have an example?

Thanks for wait, not sure why I didn't get notification for this.
I solved it like so and it seems to work with both Livewire and just Alpine:

// boilerplate code before...
        this.$watch(
            "value",
            function (newDate) {
                pickr.setDate(newDate);
            }.bind(this)
        );

        window.addEventListener("newDate", (e) => {
            // Dirty workaround to set min-dates for dependant pickers.
            // It works by passing through new min date and receiver.
            // Since Flatpickr doesn't support dynamic properties, it should
            // just get rebuilt.
            const {
                detail: { receiver },
            } = e;
            const {
                detail: { date },
            } = e;

            if (this.$el.firstElementChild.name !== receiver) {
                return;
            }

            pickr = flatpickr(this.$refs.datepicker, {
                ...config,
                ...{ minDate: date },
            });
        });
    },
//...
  }));

@ZermattChris
Copy link

ZermattChris commented Jan 13, 2024

This is how I've got it working (with some help from Copilot):

<div x-data="datepicker(@entangle($attributes->wire('model')))" class="relative">
    <div class="flex flex-col">
        <div>
            <input type="date" x-ref="myDatepicker" x-model="value" placeholder="dd-mm-yyyy"
                class="block w-full rounded-md border-0 py-2 pl-10 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
            >
            <span class="cursor-pointer underline" x-on:click="reset">
            </span>
        </div>
    </div>
</div>

@once
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
    <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>

    <script>
        document.addEventListener('alpine:init', () => {
            Alpine.data('datepicker', (model) => ({
                value: model,
                init(){
                    this.pickr = flatpickr(this.$refs.myDatepicker, {
                        // Options here
                        minDate: "today",
                        dateFormat: "Y-m-d"
                    });

                    this.$watch('value', function(newValue){
                        this.pickr.setDate(newValue);
                    }.bind(this));
                },
                reset(){
                    this.value = null;
                }
            }))
        })
    </script>
@endonce

Hope this helps someone :-)

@englishpigdog
Copy link

I've run into an issue with multiple datepickers on the same page - they both preload with the same value when the component properties both have an existing values. I think it's something to do with the x-model being the same (x-model="value") for both datepickers.

I've spent hours trying to solve this with my very limited alpine skills :(

image

<div wire:ignore x-data="datepicker(@entangle($attributes->wire('model')))" class="relative">    
    <x-input class="mt-1 block w-full" x-ref="myDatepicker" x-model="value" id="{{ rand() }}" />    
</div>

@once
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
    <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>

    <script>
        document.addEventListener('alpine:init', () => {
            Alpine.data('datepicker', (model) => ({
                value: model,
                init(){
                    this.pickr = flatpickr(this.$refs.myDatepicker, {dateFormat:'d/m/Y'})

                    this.$watch('value', function(newValue){
                        this.pickr.setDate(newValue);
                    }.bind(this));
                },
                reset(){
                    this.value = null;
                }
            }))
        })
    </script>
@endonce
<div class="mb-5" wire:ignore>        
    <x-label value="Date" />
    <x-datepicker wire:model="date" />
    <x-input-error for="date" class="mt-2" />
</div>  

<div class="mb-5" wire:ignore>        
    <x-label value="Today" />
    <x-datepicker wire:model="today" />
    <x-input-error for="today" class="mt-2" />
</div>

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