F
Filament15mo ago
qcol

Textinput value like Placeholder content?

In Placeholder::make('name')->content() I can "listen" to the other live() fields of the form and dynamically change the value of content depending on the other fields. How to do similarly with the value of the Textinput field? I know that during form initialization I can use ->default() but I am concerned with editing the form.
19 Replies
Patrick Boivin
Patrick Boivin15mo ago
You can use a standard closure for the content:
->content(fn ($get) => $get('my_field'))
->content(fn ($get) => $get('my_field'))
qcol
qcolOP15mo ago
I know how to do it with Placeholder, however, my question is about TextInput. How do you fill a TextInput with a value just like you fill a Placeholder with ->content()? Method ->content() doesn't exist for TextInput.
Patrick Boivin
Patrick Boivin15mo ago
Ok, my bad, I did miss the point of the question. It's a bit different with input fields, you'll need to use afterStateUpdated() and $set(). Check the second example in the link I shared 👆
qcol
qcolOP15mo ago
hmmm... And there is no way to get data from other fields using $get to this field? That would be much more convenient because it would be done from one place. When injecting data from other fields ($set), you have to perform an action on each of those fields, so a lot more operations. Also (and this is the worst), afterStateUpdated only works with user input. It doesn't work with data coming from $set and php (so says the documentation). There are times when the data comes from $set or PHP, what then?
I'm a bit confused because awcodes recently wrote that it is possible to reverse using afterStateHydrated, i.e. retrieve data with $get and not transfer from other fields with $set. https://discord.com/channels/883083792112300104/1143657714086580264/1144196863428399177 Unfortunately either I misunderstood this or it doesn't work. The topic is died, probably because I clicked "Solved" too soon 🙂
Patrick Boivin
Patrick Boivin15mo ago
I think there are probably a couple of different ways to approach this, but I don't have enough information on what you're trying to do exactly. afterStateHydrated() is a good hook but it's only executed once, when the form is initialized. I agree with you that $get() and $set() is not always super convenient when a form is very dynamic, but it works.
qcol
qcolOP15mo ago
In this example I can put qty or price manually and Total is calculated. I can even use Select to send price by $set and Total is calculated live... I want the same but with Textinput, not Placeholder public static function form(Form $form): Form { return $form ->schema([ Select::make('Product')->options([1 => 'product1', 2 => 'product2', 3 => 'product3']) ->afterStateUpdated( function($state, $set) { if ($state == 1) $set('price',221); elseif ($state == 2) $set('price',332); elseif ($state == 3) $set('price',445); })->live(), TextInput::make('qty')->live(), TextInput::make('price')->live(), Placeholder::make('total') ->content(function (Get $get) { return $get('qty') * $get('price'); }) ]); }
Patrick Boivin
Patrick Boivin15mo ago
Do you want your users to be able to edit the TextInput?
qcol
qcolOP15mo ago
Yes, sometimes TextInput will be disabled() (just for total preview), sometimes user should edit it manually. Of course, in this particular case it does not make sense, but I have other such cases where it is needed. In this example, even if I used afterStateUpdated() on the qty and price fields, sending the result using $set('total') then there are 2 problems: 1. I have to repeat the same action 2 times on both fields and send the result to total 2. Select won't work here (because afterStateUpdated() only works when entering the price manually.
Patrick Boivin
Patrick Boivin15mo ago
If you're worried about duplication, you can always extract the closure:
$calculateTotal = function ($get, $set) {
$set('total', $get('qty') + $get('price'));
};

return $form->schema([
Forms\Components\TextInput::make('qty')
->afterStateUpdated($calculateTotal)
->live(),
Forms\Components\TextInput::make('price')
->afterStateUpdated($calculateTotal)
->live(),
Forms\Components\TextInput::make('total'),

//...
$calculateTotal = function ($get, $set) {
$set('total', $get('qty') + $get('price'));
};

return $form->schema([
Forms\Components\TextInput::make('qty')
->afterStateUpdated($calculateTotal)
->live(),
Forms\Components\TextInput::make('price')
->afterStateUpdated($calculateTotal)
->live(),
Forms\Components\TextInput::make('total'),

//...
Dennis Koch
Dennis Koch15mo ago
OtherField::()
->live()
->afterStateUpdated(fn (Set $set) => $set('your-textfield', 'new-value'))
OtherField::()
->live()
->afterStateUpdated(fn (Set $set) => $set('your-textfield', 'new-value'))
Oh lol. Didn't see there were answers already 🤦🏼‍♂️
Patrick Boivin
Patrick Boivin15mo ago
The more the merrier 😄
qcol
qcolOP15mo ago
Yes, admittedly it can be simplified but these fields are sometimes more than 2.... Well, and the Select field would also need to be extended by another recalculation, because just sending the price to the price field is not enough, you also need to send the result here to the total. It gets a bit complicated, I wonder, from a technical point of view, why in Placeholder it can be done with $get in ->content() and with TextInput it can't? Why doesn't TextInput have a method such as ->value() ?
Patrick Boivin
Patrick Boivin15mo ago
@qcol You can apply the same idea for the Select:
$calculateTotal = function ($get, $set) {
$set('total', $get('qty') + $get('price'));
};

$handleSelect = function ($state, $get, $set) use ($calculateTotal) {
if ($state == 1) $set('price',221);
elseif ($state == 2) $set('price',332);
elseif ($state == 3) $set('price',445);

$calculateTotal($get, $set);
};

Select::make('Product')
->options([ ... ])
->afterStateUpdated($handleSelect)
->live(),
$calculateTotal = function ($get, $set) {
$set('total', $get('qty') + $get('price'));
};

$handleSelect = function ($state, $get, $set) use ($calculateTotal) {
if ($state == 1) $set('price',221);
elseif ($state == 2) $set('price',332);
elseif ($state == 3) $set('price',445);

$calculateTotal($get, $set);
};

Select::make('Product')
->options([ ... ])
->afterStateUpdated($handleSelect)
->live(),
Why doesn't TextInput have a method such as ->value() ?
Because it's not that simple i guess... there are already ->default() ->afterStateHydrated() ->formatStateUsing()... where does ->value() fit into this?
qcol
qcolOP15mo ago
but ->default() and ->afterStateHydrated() work only when form is initiated?
Patrick Boivin
Patrick Boivin15mo ago
Sure, but I still don't know where ->value() would fit in the field's lifecycle If you have some time, I guess you could try to work on TextInput, see if you can extend it to fit your use-case. If you find a great solution, send a PR. It's possible that it can be improved.
qcol
qcolOP15mo ago
OK, Thank you very much for your help, now I know for sure that I have to do it differently. I thought there was something I didn't know or something I was doing wrong. Time to get to work. Thanks again! 🙂
Patrick Boivin
Patrick Boivin15mo ago
You're welcome!
qcol
qcolOP15mo ago
Oh, I'm too weak for such things.... Maybe someday? 🙂
Want results from more Discord servers?
Add your server