skip to Main Content

I am working with Filament and Laravel and currently I’ve got these two field inputs for choosing City BASED ON Province Field, inside form() method:

Select::make("meta.employment_state")
        ->label("Province")
        ->getSearchResultsUsing(fn (string $search): array => State::query()->where('name', 'like', "%{$search}%")->limit(10)->pluck('name', 'id')->toArray())
        ->getOptionLabelUsing(fn ($value): ?string => State::find($value)?->name)
        ->afterStateUpdated(function (Set $set) {
            $set('meta.employment_city', null);
        })
        ->live()
        ->searchable()
        ->extraAttributes(['x-data' => '{ selected: false }', 'x-on:change' => 'selected = $event.target.value !== ""'])
        ->dehydrated(false),

    Select::make("meta.employment_city")
        ->label("City")
        ->getSearchResultsUsing(fn (string $search, Get $get): array => City::query()->whereRelation('state', 'id', '=', $get('meta.employment_state'))->where('name', 'like', "%{$search}%")->limit(10)->pluck('name', 'id')->toArray())
        ->getOptionLabelUsing(fn ($value): ?string => City::find($value)?->name)
        ->placeholder('انتخاب کنید')
        ->searchable()
        ->extraAttributes(['x-show' => 'selected', 'x-cloak' => true]),

Basically What I wanna do is that to hide the 2nd input which is city input till the 1st input is getting filled.

This must be done dynamically so I added JS attributes in order to make thing but not working and still shows the City input without the need of filling of province input and this is wrong.

So how to do this with Filament ?

2

Answers


  1. You don’t need to add extra attributes to hide fields. You can use Filament’s visible() or hidden() (docs) methods, combined with $get to get the Form state. Something like this should work:

    Select::make("meta.employment_state")
        ->label("Province")
        ->getSearchResultsUsing(fn (string $search): array => State::query()
            ->where('name', 'like', "%{$search}%")
            ->limit(10)
            ->pluck('name', 'id')
            ->toArray()
        )
        ->getOptionLabelUsing(fn ($value): ?string => State::find($value)?->name)
        ->afterStateUpdated(function (Set $set) {
            $set('meta.employment_city', null);
        })
        ->live()
        ->searchable()
        ->dehydrated(false),
    
    Select::make("meta.employment_city")
        ->label("City")
        ->getSearchResultsUsing(fn (string $search, Get $get): array => City::query()
            ->whereRelation('state', 'id', '=', $get('meta.employment_state'))
            ->where('name', 'like', "%{$search}%")
            ->limit(10)
            ->pluck('name', 'id')
            ->toArray()
        )
        ->getOptionLabelUsing(fn ($value): ?string => City::find($value)?->name)
        ->placeholder('انتخاب کنید')
        ->searchable()
        ->visible(fn (Get $get) => !empty($get('meta.employment_state'))), 
    
    Login or Signup to reply.
  2. This is hacky, but when it works it works.. ok?

    You are on the right path. I’ve done this a few times using alpine and filament’s extraAttributes methods.

    You’ll need to treat the elements exactly like you would if you were writing the html/alpine attributes yourself: include x-data on the parent element that wraps elements you want to be dynamic.

    The problem with your code is that the alpine elements don’t know about each other. You’ll either need to use $dispatch and handle the events from the window on each element, or you’ll need to wrap both elements in another that has your x-data setup.

    When in doubt, look at the html generated by filament.

    Here’s a working example in my filament panel:

    return $form->extraAttributes(['x-data' => json_encode([
            'search' => '',
            'init'=>"() => {
                $watch('search', (value) => {
                    $dispatch('searched', value)
                })
            }"
        ])])->schema([
    
        TextInput::make('search')->extraAlpineAttributes(['x-model' => 'search']),
    
        ...collect($items)
            ->map(function ($item) {
                return Toggle::make($item['name'])
                    ->label($item['title'])
                    ->extraAlpineAttributes([
                        '@searched.window' => '$dispatch("showing", "'.$item['title'].'".includes($event.detail))'
                    ])
                    ->extraFieldWrapperAttributes([
                        'x-show' => "show",
                        '@showing' => 'show = $event.detail',
                        'x-data' => '{show: true}',
                    ]);
            })
    ]);
    

    NOTE: unless you’re using the extraAlpineAttributes method, your values will be html encoded, like the image below. I haven’t found a way to avoid this. As a workaround, I’m dispatching an event from the input element (because extraAlpineAttributes wont encode my quotes) to the element wrapper, which hides updated the wrapper show prop.

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search