-
-
Save jonneroelofs/b4be067329cb12777c4fcf2658013bc2 to your computer and use it in GitHub Desktop.
<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'); | |
} | |
} |
Great to hear. I was already considering doing a follow up video where I show how you can pass configuration to Flatpickr (I assume you came here from Youtube?).
For reference Wrapping Flatpickr .
I think your question is nice angle for a follow up video. Will put that on my video idea list.
You wil actually only need to bind 1 variable I think, like $myRange and then explode the result into 2 different variables which you then store on you eloquent model.
There is an issue with your implementation (which, I have absolutely no clue, how does it even work for you, but it absolutely didn't for me, when I wasn't using wire:model
to set dates).
When you instantiate flatpickr and assign it to this.pickr
within the SAME Alpine component you will reuse, you essentially re-evaluate all datepickers used from that Blade component on the page, since $watch
callback is acting upon this.pickr
instance which is shared across the page (as you will most likely use multiple datepickers).
Easy fix would just be assigning it as a local const and use it like that on watcher you have set-up. It continues to work flawlessly for me after.
@davisngl do you have an example?
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 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 },
});
});
},
//...
}));
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 :-)
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 :(
<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>
This was immensely helpful for getting flatpickr working with livewire! I'm thrilled to be able to use that library again. Any idea how this should be tweaked to utilize the range feature of flatpickr?
The idea would be that I have a
$eloquentModel->start_at
and$eloquentModel->end_at
that would be dates representing a range.