Closed swarakaka closed 2 years ago
There are two problems.
It is impossible to use "includeExternalScripts
" with a conditional field.
The external script is pushed to 'scripts
' if the value is true
, when Livewire mounts the component, and removed when the value becomes false
, (and it can't be pushed later).
The other problem is that the component won't load "birthday
" and "certificate
" if the condition is false when the component is mounted.
To get around it you will have to
Flatpickr
script in your app.js
and remove the "includeExternalScripts
" in your field declaration. This also applies to Flatpickr locales.birthday
" and "certificate
" in your updatedStaffId
method public function updatedStaffId($value)
{
if(filled($value) && ($value === "2" || $value === "5")){
$this->show_birthday_and_certificate = false;
} else {
data_set($this, 'form_data.birthday', ($this->model->birthday ?? "") );
data_set($this, 'form_data.certificate', ($this->model->certificate ?? "") );
$this->show_birthday_and_certificate = true;
}
}
Thank you for your answer, the problem wasn't solved, but it's the same.
@swara-mohammed I tested your code and there is a bug. Will work on it today.
Hello again @swara-mohammed
I figured out what is going wrong here :)
When Livewire hides the DatePicker
, FlatPickr
doesn't destroy the instance, so "half" of the html is still in the DOM.
Alpine cant find it's x-data
, because it is removed.
I have a wire:ignore
set on the FlatPickr
div
, which I can't remove because Livewire will destroy the instance on each render...
So the only way I can figure out at this moment is to add class hidden
to the field root.
This means that you can use ->includeExternalScripts()
.
Also please install latest version, I found a bug when merging rootAttr with an empty array.
When hiding the field with classes, the data properties will always exist in $form_data
and $validated_data
This is important for you to know in onCreateModel($validated_data)
and onUpdateModel($validated_data)
You might want to filter out those properties when saving your component. Depending on the state of $show_birthday_and_certificate
Suggestion for your component.
<?php
namespace App\Http\Livewire\Forms\Persons;
use App\Models\Person;
use App\Models\Staff;
use Illuminate\Support\Collection;
use Tanthammar\TallForms\Input;
use Tanthammar\TallForms\Select;
use Tanthammar\TallForms\TallFormComponent;
use Tanthammar\TallFormsSponsors\DatePicker;
use Tanthammar\TallFormsSponsors\Email;
use Tanthammar\TallFormsSponsors\Tel;
class CreateOrUpdatePerson extends TallFormComponent
{
public Collection $breads;
public string $formTitle = '';
public array $staffs = [];
public bool $show_birthday_and_certificate = true;
public function mount(?Person $person)
{
//Gate::authorize()
$this->formTitle = $person->exists ?
__('Edit :item ', ['item' => $person->name])
: __('Add :model ', ['model' => __('Person')]);
$this->breads = new Collection([
['text' => __('Persons'), 'url' => 'persons'],
['text' => __($this->formTitle)],
]);
// set the checkboxes options
$this->staffs = Staff::orderBy('id', 'desc')->pluck('id', 'name')->all();
$this->mount_form($person); // $person from hereon, called $this->model
}
protected function formAttr(): array
{
return [
'formTitle' => $this->formTitle,
'wrapWithView' => true,
'showDelete' => false,
'inline' => true
];
}
protected function onCreateModel($validated_data): void
{
Person::create($this->filterValidatedData($validated_data));
}
protected function onUpdateModel($validated_data): void
{
$this->model->update($this->filterValidatedData($validated_data));
}
protected function filterValidatedData($validated_data): array
{
return $this->show_birthday_and_certificate ? $validated_data : \Arr::except($validated_data, ['birthday', 'certificate']);
}
//optional method, identical to parent
protected function onDeleteModel()
{
$this->defaultDelete();
}
public function updatedStaffId($value): void
{
if (($value === "2" || $value === "5") && filled($value)) {
$this->show_birthday_and_certificate = false;
} else {
$this->show_birthday_and_certificate = true;
}
}
protected function fields(): array
{
return [
Select::make(__('Staff'), 'staff_id')
->options($this->staffs, false) //I think your data is [ $id => $label ]? If not, remove false, or change pluck()
->rules('required'),
Input::make(__('Name'), 'name')
->rules('required')
->autocomplete('new-text'),
Email::make(__('Email'), 'email')
->autocomplete('new-email'),
Tel::make(__('Phone'), 'phone')
->autocomplete('new-tel'),
DatePicker::make(__('Birthday'), 'birthday')
->rootAttr($this->show_birthday_and_certificate ? [] : ['class' => 'hidden'], true)
->locale('en')
->placeholder('Select a date...')
->includeExternalScripts(), //only included once, if multiple DatePicker fields
Input::make(__('Certificate'), 'certificate')
->rootAttr($this->show_birthday_and_certificate ? [] : ['class' => 'hidden'], true)
->rules('nullable')
->autocomplete("off"),
Input::make(__('Address'), 'address')
->rules('nullable')
->autocomplete('new-address'),
];
}
}
I will add this to the documentation as well.
Thank you for your solution, but I felt another bug. I'll write the solution below.
implode(): Argument #2 ($array) must be of type ?array, string given
protected function mergeClasses(string $key, array $custom): void
{
$merged = array_merge_recursive($this->attributes[$key], $custom);
if (Arr::has($merged, 'class')) {
$merged['class'] = implode(" ", $merged['class']);
}
$this->attributes[$key] = $merged;
}
solved:
protected function mergeClasses(string $key, array $custom): void
{
$merged = array_merge_recursive($this->attributes[$key], $custom);
if (Arr::has($merged, 'class') && is_array($merged['class'])) {
$merged['class'] = implode(" ", $merged['class']);
}
$this->attributes[$key] = $merged;
}
@swara-mohammed Yes, that is why I wrote you need to upgrade to the latest version. I discovered that bug when I tested your component. :)
Returns an error when changing a select several times.