Hooks for nodejs smart actions

Feature(s) impacted

I’m trying to use @forestadmin nodejs library to implement smart-actions and I found out that load hook is not available there for some reason, so I can’t manipulate with fields array after I open smart-action for a new row.

Observed behavior

In console I see call to the POST /_actions/collection/1/smart-action-name/hooks/load, but I can’t see any documentation or usages in the source code.

Expected behavior

Able to use load hook to modify the smart-action.

Context

  • Project name: not sure it is needed in context of this question
  • Team name: …
  • Environment name: local
  • Agent technology: nodejs
  • Agent (forest package) name & version: “@forestadmin/agent”: “^1.41.7”, “@forestadmin/datasource-customizer”: “^1.45.6”, “@forestadmin/datasource-sql”: “^1.14.11”, “@forestadmin/datasource-toolkit”: “^1.34.4”,
  • Database type: postgresql
  • Recent changes made on your end if any: ?

Question

How can I use hooks that are called from smart-action?
Either I’d like to be able to use hooks for the smart-action, or be able to achieve creating fields dynamically while the form loads. Haven’t found a solution to this so far.

Hello @nike1v and welcome to the community !

The documentation for the @forestadmin/agent is located in a different url that you might be familiar with, here it is. And more specifically you will be able to find everything you need to create your dynamic actions in this section.

Best regards,

Thank you for a quick reply, I do know that documentation, but there is no such a thing about smart-action hooks, so I’m here asking about them. With current Dynamic actions section I see only 3 properties for an action (execute, scope, form), but the library does calls /hooks/load request. So how can I access that one?

The call that you see is what will trigger the different function properties in your form definition. This allows to have specific handling functions per field and field property (isRequired, isReadOnly, value…)

I can’t manipulate with fields array after I open smart-action for a new row.

If you wish to display a field based on a condition you can look at the example 2 of the documentation.

The example 2 will be suitable when I have pre-defined form fields for the smart-action. But in my case I want them to be created based on a record field. Is it possible with current API?

Does this implementation not work out for you ?

   {
        label: 'A field',
        type: 'String',
        if: async context => {
          const user = await context.getRecord(['firstName', 'lastName']);

          return user.firstName && user.lastName;
        },
   },
```

That’s not quite what I’m looking for. That implementation works fine in a case where I do need to show a field from existing form array, but in my case I’m trying to generate form array whenever the smart-action loads.
Example:

  private async generateDynamicFields(context: ISmartActionContext<TSchema, 'PdfForm'>) {
    if (context.getRecordId() === undefined) {
      this.form = this.defaultForm;
      return true;
    }

    const [contextFields, prefillVariables] = await Promise.all([
      context.getRecord(['fields']),
      this.prefillFormService.getPrefillVariables(),
    ]);
    const fields: FormField[] = contextFields.fields;
    const items = fields.map((field) => this.createActionFieldFromFormField(field, prefillVariables));

    this.form = [this.defaultForm[0], ...items];
    return false;
  }

In the example I’m trying to populate this.form array
(which has type of DynamicField<ISmartActionContext<TSchema, 'PdfForm'>>[])
with new fields that will be generated based on a record of a current opened row.
This is a Single scope smart-action.

The current implementation does work in some way, but I do have to reload a page 2 times before fields will be displayed, this is also gives some unclear thoughts when you open another record, but the smart-action populated with old fields (because it doesn’t reset itself when you close it by canceling and I do not have options to reset it manually or pre-populate the form array before form is displayed.

Thanks for the detailed information. As you’ve probably guessed this is not possible with the current implementation.

By digging with the team I have found both an ugly way to override the behaviour of actions and a draft PR to support dynamic field creation in action forms.

The override is not a suitable solution and direction I would like to push. I will bring back the subject of native support of dynamic forms to the team.

Ok, got it! Thanks for a productive conversation!

1 Like

Hello @nike1v,

We have decided to release the ability to dynamically create the action form. You should be able to use it with @forestadmin/datasource-customizer 1.46.0

However I would like to inform you that there initially was a good reason that made the team hesitant to proceed with it.
Every time the load hook is called on your action the form will be recomputed. This could lead to performance issues depending on your implementation as well as stability issues in the usage of the action if you handle the lifecycle of a field in multiple hooks.

When using the dynamic form creation;

  • I would suggest limiting the use of dynamic properties inside of a field definition, even more so if it does some DB calls. Centralising the form lifecycle in your initial function would be the best in terms performance.
  • If your form is dynamic only in its creation and remains the same afterwards, you can make use of the context.formValues which would contain the form after the first render. Avoiding unnecessary calls to your DB.

I hope you will find this satisfying.

Best regards,

3 Likes