Select Value “Other” with Free-Form Text Input

2024-10-08

Sometimes, we need "Other" as an option in the Select, allowing the user to enter any value they want, in a separate text field. Let's see how to implement it.


The Scenario

You have a form with a list of cities, but the last option is Other. When the other is selected, a text input appears where the user can enter his city.

Visually, this is how the form looks like:

Migrations for cities and shops looks like this:

database/migrations/xxx_create_cities_table.php:

Schema::create('cities', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});

database/migrations/xxx_create_shops_table.php:

Schema::create('shops', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('city_id')->nullable()->constrained();
$table->string('city_other')->nullable();
$table->timestamps();
});

How do we build this form?


The Solution

First, the cities select input. The input must be live. We generate the options manually instead of from a relationship. We need it this way because we need to add the other option manually. Finally, when the option other is selected, the field should not be dehydrated.

use App\Models\City;
use Filament\Forms;
 
Forms\Components\Select::make('city_id')
->live()
->label('City')
->options(City::pluck('name', 'id')->toArray() + ['other' => 'Other'])
->dehydrated(fn ($state) => $state !== 'other'),

Then, the text input is used to enter the names of other cities. It should be visible only when the selected city is other. We can do it using $get(). Finally, this field is required only when the city_id select has a value of other.

use Filament\Forms;
 
Forms\Components\TextInput::make('city_other')
->visible(fn (Forms\Get $get) => $get('city_id') === 'other')
->requiredIf('city_id', 'other'),

This is what the code for the whole form looks like:

use App\Models\City;
use Filament\Forms;
use Filament\Forms\Form;
 
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->columnSpanFull(),
Forms\Components\Select::make('city_id')
->live()
->label('City')
->options(City::pluck('name', 'id')->toArray() + ['other' => 'Other'])
->dehydrated(fn ($state) => $state !== 'other'),
Forms\Components\TextInput::make('city_other')
->visible(fn (Forms\Get $get) => $get('city_id') === 'other')
->requiredIf('city_id', 'other'),
]);
}

We can also save the city from the text input to the cities list. This will select the proper city in the edit page and allow other users to choose this city. For this, we can use the afterCreate() hook in the CreateRecord page.

use App\Models\City;
use App\Filament\Resources\ShopResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
 
class CreateShop extends CreateRecord
{
protected static string $resource = ShopResource::class;
 
public function afterCreate(): void
{
if ($this->data['city_id'] === 'other') {
$city = City::create([
'name' => $this->data['city_other'],
]);
 
$this->record->update([
'city_id' => $city->id,
]);
}
}
}

A few of our Premium Examples: