Search code examples
laravellaravel-filamentfilamentphp

How to make Repeater item count dynamic in Laravel Filament V3?


In Laravel Filament I want to increase or decrease the numbers of repeater items on change the value in count InputText. The live() method is not reactive in this case.

TextInput::make('count')
    ->numeric()
    ->live()
    ->default(3),
Repeater::make('installments')
    ->schema([
        TextInput::make('amount')->numeric()->placeholder('Installment Amount')->hiddenLabel(),
        TextInput::make('interval')->numeric()->placeholder('Interval')->hiddenLabel(),
        Select::make('interval_unit')->hiddenLabel()->placeholder('Interval Unit'),
    ])
    ->defaultItems(function (Get $get) {
        return $get('count');
    })
    ->hiddenLabel()
    ->deletable(false)
    ->addable(false)
    ->columnSpan(3)
    ->reorderable(false)
    ->columns(3),

In the initial load it displays 3 items as default value of count is 3 but changing the value of count in the TextInput field does not change the number of repeater items.

How do I make this repeater items dynamic?


Solution

  • I would do something like this:

    TextInput::make('count')
        ->numeric()
        ->live()
        ->default(3)
        ->minValue(0)
        ->afterStateUpdated(function (Set $set, Get $get, ?int $state, ?int $old): void {
            if (is_null($state)) {
                $set('installments', []);
                return;
            }
    
            $installments = $get('installments');
            $itemsToBeAdded = $state - $old;
    
            if ($itemsToBeAdded > 0) {
                for ($i = 0; $i < $itemsToBeAdded; $i++) {
                    $installments[] = [];
                }
            } else if ($itemsToBeAdded < 0) {
                for ($i = 0; $i < abs($itemsToBeAdded); $i++) {
                    array_pop($installments);
                }
            }
    
            $set('installments', $installments);
        }),
    Repeater::make('installments')
        ->schema([
            TextInput::make('amount')->numeric()->placeholder('Installment Amount')->hiddenLabel(),
            TextInput::make('interval')->numeric()->placeholder('Interval')->hiddenLabel(),
            Select::make('interval_unit')->hiddenLabel()->placeholder('Interval Unit'),
        ])
        ->defaultItems(function (Get $get) {
            return $get('count');
        })
        ->hiddenLabel()
        ->deletable(false)
        ->addable(false)
        ->columnSpan(3)
        ->reorderable(false)
        ->columns(3),
    

    What I changed?

    • I added the afterStateUpdated function, where I retrieve the current state of both count and installments (using $get) and I calculate whether we need to add or remove an item (using $set).
    • And last but not least, do not let the minValue of count to be less than 0 by chaining the ->minValue(0) line.

    Docs: