F
Filament2mo ago
Gaurav

Calculated input field in a form

I wish to include a calculated input field in a form, which is sum of a number of fields, some of which are dynamically included based on an attribute of the record. Although, I would calculate the value in the backend, I want to have the calculated value shown in the form itself while creation. I've tried evaluate() method as well as state() method both of which give Typed property Filament\Forms\Components\Component::$container must not be accessed before initialization Is there a way to include such calculated fields? Alternatively, how I can I include custom javascript only for the form of a particular resource? As then I can calculate the field value via javascript.
Solution:
have you tried a Placeholder with content(function () {}) ?
Jump to solution
36 Replies
Solution
Dan Harrin
Dan Harrin2mo ago
have you tried a Placeholder with content(function () {}) ?
Gaurav
Gaurav2mo ago
Not yet. Thanks for the suggestion. BTW, I tried afterStateUpdated on all the contributing fields, but the value doesn't get updated in the result field without a refresh (such as navigating to other steps in the wizard)
Dan Harrin
Dan Harrin2mo ago
its better to do it the other way round where the field calculates its own state based on others instead of other fields setting placeholder lets you do that
Gaurav
Gaurav2mo ago
But I need this field's value to be updated whenever any of the field's value is changed. I guess injecting javascript is the only way then. However, I'm not able to figure out how to do it only for specific form or field, and not the entire panel.
Dan Harrin
Dan Harrin2mo ago
make the fields that get changed live() and this will recacluate the placeholder value each time
Gaurav
Gaurav2mo ago
Ok, thanks! This works to an extent. But the update becomes a problem, as the field is populated and hence placeholder changes are not visible. I think one way to go about it is creating a pseudo field instead which will never be posted, nor be updated from the record.
Dan Harrin
Dan Harrin2mo ago
i dont understand what the problem is? the placeholder will be calculated when the form is first loaded
Gaurav
Gaurav2mo ago
I am talking about record update, when the field will have a value and hence placeholder won't be visible
Dan Harrin
Dan Harrin2mo ago
placeholder fields can be added to the edit page too
Gaurav
Gaurav2mo ago
Wait, did you mean a field with a placeholder, or a Placeholder field? Ah, my bad. You were referring to Component/Placeholder and I was working with TextInput->placeholder() A strange issue though. I'm keeping all these dynamic fields in a JSON column in the db, titled data. The column is correctly cast to array, and the data is getting stored in this column alright. However, when updating the record, the corresponding fields such as data.quantity are not getting populated. I tried changing the cast to object instead of array as well.
Dan Harrin
Dan Harrin2mo ago
it definitely should be array please send your schema
Gaurav
Gaurav2mo ago
TextInput::make('data.quantity')
->numeric()
->live(onBlur: true)
->inputMode('decimal')
->rules('numeric|min:0')
TextInput::make('data.quantity')
->numeric()
->live(onBlur: true)
->inputMode('decimal')
->rules('numeric|min:0')
Dan Harrin
Dan Harrin2mo ago
the whole thing are you using an edit page on a resource or is this a custom form
Gaurav
Gaurav2mo ago
I'm using simple format, so create / edit / view all are in the pop-up
Dan Harrin
Dan Harrin2mo ago
in action modals? can you send the entire resource then in a github gist? and the model please if i cant spot anything i'd like you to open an issue with a reproduction repository (a new project with minimal code to reproduce the problem)
awcodes
awcodes2mo ago
Could this be a conflict with the base state path also being called ‘data’ like it’s looking for data.quantity instead of data.data.quantity?
Gaurav
Gaurav2mo ago
Gist
Resource Hierarchy to Allow Multiple Transaction Types
Resource Hierarchy to Allow Multiple Transaction Types - SaleResource.php
Dan Harrin
Dan Harrin2mo ago
if its in a modal the state path isnt data, and even so i dont think that would result in this behaviour
awcodes
awcodes2mo ago
fair, just a thought.
Dan Harrin
Dan Harrin2mo ago
the Sale model please
Gaurav
Gaurav2mo ago
Let me give you both the models, as Sale is derived from Transaction
class Sale extends Transaction
{
protected $table = 'transactions';

protected static function boot()
{
parent::boot();
static::creating(function ($transaction) {
$transaction->category = 'Sale';
});
}
}
class Sale extends Transaction
{
protected $table = 'transactions';

protected static function boot()
{
parent::boot();
static::creating(function ($transaction) {
$transaction->category = 'Sale';
});
}
}
Dan Harrin
Dan Harrin2mo ago
also where is getschema called
Gaurav
Gaurav2mo ago
class Transaction extends Model
{
// ...
/**
* The attributes that should be cast.
*
* @var array|string[]
*/
protected $casts = [
'date' => 'date',
'amount' => 'float',
'is_system' => 'boolean',
'data' => 'array',
];

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'account_id',
'bank_cash_account_id',
'category',
'number',
'date',
'asset_class',
'amount',
'is_system',
'notes',
'data',
];

/**
* The "booted" method of the model.
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::creating(function ($transaction) {
if (empty($transaction->number)) {
$transaction->number = $this->generateTransactionNumber();
}
if (!isset($transaction->amount)) {
$amount = 0;
if (($data = $transaction->data)) {
$base_amount = ($data['price'] ?? 0) * ($data['quantity'] ?? 1);
$charges = ($data['brokerage'] ?? 0) + ($data['stamp_duty'] ?? 0) + ($data['other_charges'] ?? 0);
$charges *= $transaction->category === 'Sale' ? -1 : 1;
$amount = $base_amount + $charges;
}
$transaction->amount = $amount;
}
});
}
}
class Transaction extends Model
{
// ...
/**
* The attributes that should be cast.
*
* @var array|string[]
*/
protected $casts = [
'date' => 'date',
'amount' => 'float',
'is_system' => 'boolean',
'data' => 'array',
];

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'account_id',
'bank_cash_account_id',
'category',
'number',
'date',
'asset_class',
'amount',
'is_system',
'notes',
'data',
];

/**
* The "booted" method of the model.
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::creating(function ($transaction) {
if (empty($transaction->number)) {
$transaction->number = $this->generateTransactionNumber();
}
if (!isset($transaction->amount)) {
$amount = 0;
if (($data = $transaction->data)) {
$base_amount = ($data['price'] ?? 0) * ($data['quantity'] ?? 1);
$charges = ($data['brokerage'] ?? 0) + ($data['stamp_duty'] ?? 0) + ($data['other_charges'] ?? 0);
$charges *= $transaction->category === 'Sale' ? -1 : 1;
$amount = $base_amount + $charges;
}
$transaction->amount = $amount;
}
});
}
}
public static function form(Form $form): Form
{
return $form
->schema(static::getSchema());
}
public static function form(Form $form): Form
{
return $form
->schema(static::getSchema());
}
I've separated schema into a separate method, as that allows me to use the same from other Resources for createOptionForm method, such as createOptionForm(SaleResource::getSchema()) Now, if my database schema changes, I can update the schema in just one place in the corresponding resource file, and need not search for it in various files which might be using createOptionForm
Gaurav
Gaurav2mo ago
Gist
Resource Hierarchy to Allow Multiple Transaction Types
Resource Hierarchy to Allow Multiple Transaction Types - SaleResource.php
Dan Harrin
Dan Harrin2mo ago
shouldnt cause an issue i cant see whats wrong, so please try in a new project to reproduce it you might find out what's wrong in the meantime
Gaurav
Gaurav2mo ago
OK, thanks for trying though. As the data is getting stored in the database, so model appears to be correct. I'm now trying to tinker with it to see if anything jumps out.
Dan Harrin
Dan Harrin2mo ago
where is the editaction?
Gaurav
Gaurav2mo ago
Using Simple layout, so there is no separate edit layout. Everything is being handled from a single page:
class ManageTransactions extends ManageRecords
{
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}
class ManageTransactions extends ManageRecords
{
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}
Dan Harrin
Dan Harrin2mo ago
how are you doing the edit then with the information preloaded? isnt there a button on each table row?
Gaurav
Gaurav2mo ago
I'm actually extending Resource class which extends Filament\Resources\Resource as BaseResource. Within that I'm using:
$table
//...
->actions([
Tables\Actions\ActionGroup::make(array_merge([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
Tables\Actions\CreateAction::make('Duplicate')
->label('Duplicate')
->icon('heroicon-o-document-duplicate')
->form(fn (Form $form) => static::form($form->model(static::$model))->columns(2))
->fillForm(static::getFillFormAction()),
Tables\Actions\DeleteAction::make(),
Tables\Actions\ForceDeleteAction::make(),
Tables\Actions\RestoreAction::make(),
], static::getActions())),
$table
//...
->actions([
Tables\Actions\ActionGroup::make(array_merge([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
Tables\Actions\CreateAction::make('Duplicate')
->label('Duplicate')
->icon('heroicon-o-document-duplicate')
->form(fn (Form $form) => static::form($form->model(static::$model))->columns(2))
->fillForm(static::getFillFormAction()),
Tables\Actions\DeleteAction::make(),
Tables\Actions\ForceDeleteAction::make(),
Tables\Actions\RestoreAction::make(),
], static::getActions())),
Yes, that's exactly what is being done
Dan Harrin
Dan Harrin2mo ago
right so you do have an editaction add ->mutateRecordDataUsing(fn ($data) => dd($data)), is the data in an array there?
Gaurav
Gaurav2mo ago
Let me try No, it's not there. I wonder why. Ahhhhh Got it Data was added to hidden fields 🤦‍♂️
Dan Harrin
Dan Harrin2mo ago
:pepelaugh:
Gaurav
Gaurav2mo ago
Yes, that solved it! Daily reminder of how stupid I can be at times 🥹
Dan Harrin
Dan Harrin2mo ago
its alright, there are a lot of layers here
Gaurav
Gaurav2mo ago
Thanks so much for your help @Dan Harrin 🙏