Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions 7.x/crud-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,9 @@ To customize, publish the lang files and edit the `date_time` array.
Any option from [air-datepicker's documentation](https://air-datepicker.com/docs) can be passed
inside the `air-datepicker` config array.

To control the picker from JavaScript (eg. link two pickers so that one excludes the dates
of the other), see [How to link two air-datepicker fields](/docs/{{version}}/crud-how-to#how-to-link-two-air-datepicker-fields).

Input preview:

![CRUD Field - Air Datepicker](https://backpackforlaravel.com/uploads/docs/fields/air-datepicker.jpeg)
Expand Down
93 changes: 93 additions & 0 deletions 7.x/crud-how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,99 @@ CRUD::field([
- We do not provide an interface to edit this relationship. We never needed it, nobody ever asked for it and it would be very difficult to create an interface that is easy-to-use and easy-to-understand for the admin. If you find yourself needing this, please let us know by opening an issue on GitHub.


<a name="how-to-link-two-air-datepicker-fields"></a>
### How to link two air-datepicker fields (PRO)

The `air-datepicker` field stores its air-datepicker instance on the visible input, and provides one JS helper to reach it:

- `bpFieldAirDatepickerInstance(fieldName)` - returns the air-datepicker instance of a field, by field name (or `false`). With the instance you can use the [full air-datepicker API](https://air-datepicker.com/docs): `dp.update()`, `dp.selectDate()`, `dp.clear()`, `dp.onSelect`, etc.

For example, let's prevent `end_date` from ever being before `start_date`, in a Create/Update form that has two single `air-datepicker` fields:

```php
CRUD::field([
'name' => 'start_date',
'label' => 'From date',
'type' => 'air-datepicker',
]);

CRUD::field([
'name' => 'end_date',
'label' => 'To date',
'type' => 'air-datepicker',
]);
```

Then add a script to the operation:

```php
// in your CrudController, inside setupCreateOperation() and/or setupUpdateOperation()
Widget::add()->type('script')->content('assets/js/admin/forms/link-dates.js');
```

```js
// assets/js/admin/forms/link-dates.js
$(function () {
// Backpack initializes its fields inside its own document-ready handler,
// registered after this one — defer with a macrotask so the instances
// already exist.
setTimeout(linkDates, 0);
});

function linkDates() {
var startDp = bpFieldAirDatepickerInstance('start_date');
var endDp = bpFieldAirDatepickerInstance('end_date');

if (!startDp || !endDp) return;

// What minDate/maxDate each picker was configured with in PHP ('' by default).
var startDefaultMaxDate = startDp.opts.maxDate;
var endDefaultMinDate = endDp.opts.minDate;

// One way: "To date" can't be before "From date"...
linkAirDatepickers(startDp, endDp, 'minDate', endDefaultMinDate);
// ...and the other way: "From date" can't be after "To date".
linkAirDatepickers(endDp, startDp, 'maxDate', startDefaultMaxDate);
}

function linkAirDatepickers(sourceDp, targetDp, limit, targetDefaultLimit) {
var originalOnSelect = sourceDp.opts.onSelect; // the field's own handler

sourceDp.opts.onSelect = function (args) {
// keep the field's built-in behavior (updates the hidden input)
originalOnSelect.apply(this, arguments);

if (args.date) {
targetDp.update({ [limit]: args.date });

// optional: clear the target if its current selection is now invalid
var targetSelected = targetDp.selectedDates[0];
if (targetSelected && (
(limit === 'minDate' && targetSelected.getTime() < args.date.getTime()) ||
(limit === 'maxDate' && targetSelected.getTime() > args.date.getTime())
)) {
targetDp.clear();
}
} else {
// source cleared: restore the originally configured limit.
// NOTE: passing null throws in air-datepicker 3.6.0 — restore ''
// (or the original value) instead.
targetDp.update({ [limit]: targetDefaultLimit || '' });
}
};

// also apply the link on page load, when editing an entry that already
// has a source value
var initialSource = sourceDp.selectedDates[0];
if (initialSource) {
targetDp.update({ [limit]: initialSource });
}
}
```


See the field docs [here](/docs/{{version}}/crud-fields#air-datepicker).

<a name="operations"></a>
## Operations

Expand Down