Skip to content

[RFC] Editing Fields Inline on Lists #5599

@kiler129

Description

@kiler129

Overview (aka. TL;DR)

Editing fields in-line/on list is a common functionality present in systems where users' workflows require editing simple textual data at-mass over multiple entities. This RFC aims to bring support of editing fields directly on index lists in EAB:

demo_simple.mp4

In-line edit UX 😃

Ignoring the obvious spreadsheet-driven workflows, there are a myriad of scenarios where in-line edits are prevalent. Examples include:

  • updating remaining amounts on a list of supplies in a laboratory
  • inputting test grades for all students who took a test
  • updating story point value on a list of tasks in a sprint

One of the concrete examples in the app I'm developing is adding common measurements to animals. The physical workflow mandates mass-entry after measurements are taken for multiple animals.

🐌 The current workflow can be seen below:

demo_many_clicks.mp4

🚀 Now, let's try with inline "edits":

demo_not_so_many_clicks.mp4

This is way faster 🎉 It offers a realtime and non-intrusive feedback to the user as well. Naturally, these two ways are not 1:1 comparable, as the full edit contains many more options, but the in-line style can eliminate the majority of "full edits".


Scope 🔍

This feature can get quite complex very quickly. To make it useful I needed to go beyond just simple editing of a text field, as from the beginning my use-case required actually adding a new related entity (as you can probably see on the video).

The planned scope:

  • a new InlineEditableField which is able to transparently submit a value to the backend
  • the field should be easy to use and pretty much a drop-in replacement for any scalar type like TextField or NumberField
  • the field should allow for editing a single scalar entity field
  • the field should be flexible enough to add new related records
  • it should support nested embeddables (to add to the continued efforts from e.g. Use filters on nested properties #4882)
  • this isn't meant to introduce a full-fledged API, thus it should utilize all fields & forms already present in users' applications. However, it should respond with proper HTTP codes etc.

Implementation 👨‍💻

What is shown in this RFC isn't a mock-up. It is a working implementation tested in my project. The code isn't PR-ready just yet, however it was written specifically with upstreaming in mind ;) The changes needed aren't extensive.

Data persistence

To support the data persistence changes need to be made to new() and edit() to support POST/PUT/PATCH with application/json payload. While using the standard POST and normal form handling seemed feasible at first, it quickly became a nightmare in our project. There were many reasons like issues with JS FormData not handling arrays, or Symfony Forms not expecting a partial update w/PATCH.
The changes to properly support this part include:

  • ~80 SLOC, reusing Form::submit()
  • a new protected function getResponseAfterAjaxSave() (draft name) method on AbstractCrudController, which serves a purpose similar to getRedirectResponseAfterSave() and provides a new display value

Field construction

Providing the new field is actually more complicated. The new fields supports the following options:

  • target controller: configured to the parent entity CRUD by default
  • target action: Action::EDIT by default, can be changed to Action::NEW
  • method: can be specified; by default uses PATCH for edit and POST for new
  • automatically include parent entity ID on edits
  • allows to include an extra array with additional data; this is indispensable when creating related entities, as there are e.g. required fields
  • the whole thing is ~200 SLOC

The frontend went through many revisions in my case. Currently, the form for each field is lazy-created as needed by a vanilla JS. The reason for that is provisioning for displaying a list with 5+ editable columns and 100+ rows. That would introduce a severe DOM pollution and a heavy backend load to render something which isn't used 99% of the time (i.e. when browsing lists). Nevertheless, it uses standard Bootstrap popovers & <form> elements. The only custom thing is supporting Esc to abort edit.
The form creation process support setting a size of the field, postfix, name of the "save" button, as well as the optional field placeholder.

Known limitations

  • the implementation by design supports only scalars (i.e. no selects or other complex things)
  • validation is delegated to the server

Rationale 👍

I saw this asked in many issues, I was able to quickly locate one: #365. In addition, this feature doesn't set a precedent due to already-present "AJAX".

The only dynamic list field present in EAB is BooleanField, which can be rendered as a "switch". While changing it in any way is out of scope for this issue, it can easily be done. The one-off code in AbstractCrudController::edit() could be removed. The current implementation will certainly be able to handle 1/0 update with just minor JS changes to BooleanField.

WDYT?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions