docs: document CRUD customization API and editor behavior - #5894
Conversation
Addresses vaadin/agentic-dx-improvement#103, which reported that the Flow CRUD documentation under-documents the component's extension points, forcing readers to inspect the jar to make an adoption decision. - Document the CrudEditor interface as a public extension point, with a non-Binder editor example that validates itself and renders a top-of-form error summary. - Specify the Save button enablement rules, the role of setDirty(), and how to keep Save enabled at all times via getSaveButton().setEnabled(true). - Document the getSaveButton()/getCancelButton()/getDeleteButton()/ getNewButton() accessors, with an example of hiding Delete for datasets that archive rather than remove records. - Collect the programmatic editor controls (edit, setOpened, setEditorPosition, setEditOnClick) into one table. - Note that CRUD has no manual row reordering, and point at grid replacement. - Explain how CrudFilter maps sorting and filtering onto a lazy backend. - Surface the no-border variant and CrudVariant on the main page, and link to the styling page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
Preview DeploymentThis PR has been deployed for preview. URL: https://docs-preview-pr-5894.fly.dev Changed pagesAdded content is highlighted in green; removed content is marked in red on each page.
Built from c6aa164 |
Vaadin.ProductName flagged "= Crud Styling". The sibling styling pages all spell out the component name in their titles, as does the CRUD index page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
|
|
||
| An editor that doesn't propagate its field changes -- one built from a composite component that wraps its fields, for example -- can leave Save permanently disabled. Call [methodname]`setDirty(true)` on the CRUD to enable it explicitly in such a case. | ||
|
|
||
| Some applications need Save to be enabled at all times. A common accessibility pattern is to let the user submit at any point and then show a summary of what needs fixing. Set the enabled state of the Save Button directly to get this: CRUD stops managing that Button's state from then on, leaving it enabled regardless of whether the editor is dirty. The editor's [methodname]`validate()` method still decides whether a save goes through. |
There was a problem hiding this comment.
Is this behavior documented in the CRUD Javadoc? If not, this might actually be a bug in the CRUD component.
There was a problem hiding this comment.
Yes, this part is documented. Crud.getSaveButton():
NOTE: State of the button set with
HasEnabled#setEnabled(boolean)will remain even if dirty state of the crud changes
and Crud.setDirty(boolean):
A dirty Crud has its editor Save button enabled. […] NOTE: editor Save button will not be automatically enabled in case its enabled state was changed with
Crud#getSaveButton()
The two @see each other, so the override is a deliberate, documented escape hatch. SaveButton.onEnabledStateChanged implements it by overriding the web component's __isSaveBtnDisabled, and Crud.onAttach re-applies it, so it survives detach/attach.
One caveat on the paragraph above this one, which is not in the Javadoc: "Validity doesn't factor into this: Save is enabled for invalid input, too." That comes from the implementation — vaadin-crud-mixin.js has __isSaveBtnDisabled(isDirty) { return !isDirty; }, and Flow's Crud.onAttach sets this.__validate = function () { return true; } so client-side validation never gates the save. Server-side CrudEditor.validate() is the only gate. Happy to drop that sentence if you would rather not pin down undocumented behavior.
- Add explicit [#save-button-state] and [#editor-button-access] anchors.
The [badge-flow]#Flow# suffix leaks into the generated heading ids
("save-button-state-flow"), so the two cross-references pointed at
anchors that don't exist. Verified in a browser: the page now has no
broken in-page links.
- Include PersonCrudEditor.java without `render`. It isn't a Component,
so there's nothing to render; the convention for a supporting class is
a plain listing, as in articles/flow/binding-data/index.adoc.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
|
|
||
| Set the Button labels through <<#localization,Localization>>, not [methodname]`setText()`: CRUD writes the localized labels onto its default Buttons, overwriting anything set that way. | ||
|
|
||
| Some datasets shouldn't allow deletion at all -- for example, records that are archived or deactivated instead of removed, so that history is preserved. Hide the Delete Button with CSS in that case. |
There was a problem hiding this comment.
This looks like a hack or a workaround. Unless it is advocated by the CRUD Javadoc, it should be stated as such, pending a better Java API for achieving the same thing.
There was a problem hiding this comment.
Agreed — it is a workaround, and the Javadoc does not advocate it. Reworded to say so, and added a note:
There's no API for removing the Delete Button, so the only way to do this at present is to hide it with CSS.
Hiding Delete Is a Workaround — Setting
display: noneisn't advocated by the CRUD API; it's the only thing that works today. Don't reach forsetVisible(false): CRUD manages the Delete Button'shiddenattribute itself, and it clears the attribute each time the editor opens for an existing item, which makes the Button reappear.
That last part is arguably a component bug worth its own ticket: Flow's setVisible(false) sets the hidden attribute (SimpleElementBindingStrategy.setElementInvisible), while __deleteButtonPropsChanged in vaadin-crud-mixin.js runs deleteButton.toggleAttribute('hidden', isNew) on every __isNew change, clearing it. So the one obvious API for this silently does not work. A setDeleteButtonVisible(boolean), or having CRUD respect the server-side visibility, would remove the need for the CSS. Want me to file that against flow-components?
- Remove the Styling section; it duplicated styling.adoc. - Drop frontend/demo/component/crud/crud-imports.ts and point the two Flow-only examples at the existing crud-editor-content.ts instead. It already imports crud, email-field, form-layout, and text-field, so no new file is needed. - Frame hiding the Delete Button as a workaround rather than a technique, and warn against setVisible(false), which CRUD undoes when the editor opens for an existing item. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
) * docs: document CRUD customization API and editor behavior Addresses vaadin/agentic-dx-improvement#103, which reported that the Flow CRUD documentation under-documents the component's extension points, forcing readers to inspect the jar to make an adoption decision. - Document the CrudEditor interface as a public extension point, with a non-Binder editor example that validates itself and renders a top-of-form error summary. - Specify the Save button enablement rules, the role of setDirty(), and how to keep Save enabled at all times via getSaveButton().setEnabled(true). - Document the getSaveButton()/getCancelButton()/getDeleteButton()/ getNewButton() accessors, with an example of hiding Delete for datasets that archive rather than remove records. - Collect the programmatic editor controls (edit, setOpened, setEditorPosition, setEditOnClick) into one table. - Note that CRUD has no manual row reordering, and point at grid replacement. - Explain how CrudFilter maps sorting and filtering onto a lazy backend. - Surface the no-border variant and CrudVariant on the main page, and link to the styling page. Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ * docs: use the CRUD product name in the styling page title Vaadin.ProductName flagged "= Crud Styling". The sibling styling pages all spell out the component name in their titles, as does the CRUD index page. Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ * docs: fix CRUD anchors and drop a stray render attribute - Add explicit [#save-button-state] and [#editor-button-access] anchors. The [badge-flow]#Flow# suffix leaks into the generated heading ids ("save-button-state-flow"), so the two cross-references pointed at anchors that don't exist. Verified in a browser: the page now has no broken in-page links. - Include PersonCrudEditor.java without `render`. It isn't a Component, so there's nothing to render; the convention for a supporting class is a plain listing, as in articles/flow/binding-data/index.adoc. Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ * docs: address review feedback on the CRUD page - Remove the Styling section; it duplicated styling.adoc. - Drop frontend/demo/component/crud/crud-imports.ts and point the two Flow-only examples at the existing crud-editor-content.ts instead. It already imports crud, email-field, form-layout, and text-field, so no new file is needed. - Frame hiding the Delete Button as a workaround rather than a technique, and warn against setVisible(false), which CRUD undoes when the editor opens for an existing item. Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ --------- Co-authored-by: Petter Holmström <petter@vaadin.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses vaadin/agentic-dx-improvement#103, which reported that the Flow CRUD documentation presents the component as a mostly turn-key "grid + generated editor" and leaves several decision-critical facts discoverable only by inspecting the jar.
Changes
articles/components/crud/index.adoc:====to===, and expanded with two new subsections:setDirty(), and how to keep Save enabled at all times.getSaveButton()/getCancelButton()/getDeleteButton()/getNewButton()/setNewButton(), with an example of hiding Delete for datasets that archive rather than remove records, plus a note thatsetText()on these buttons is overwritten by localization.CrudEditor<E>as a public extension point, with a table of when CRUD calls each method and a live non-BinderCrudEditorexample that validates itself and renders a top-of-formrole="alert"error summary.edit()/EditMode,setOpened(),setEditorPosition(), andsetEditOnClick()into one table.CrudFilter's constraint and sort-order maps translate intoWHERE/ORDER BY, theDataProvider<E, CrudFilter>requirement enforced byCrudGrid, and the existingPersonDataProvideras the reference implementation.no-bordervariant andCrudVarianton the main page and links to the styling page, which the main page didn't link at all.New examples:
PersonCrudEditor.java,CrudCustomEditor.java,CrudEditorButtons.java, andcrud-imports.ts(preimport for the Flow-only demos).Two corrections to the ticket
Everything was checked against the sources for
vaadin-crud-flow25.2.1 / 25.3.0-alpha8,@vaadin/crud, and Flow's element binding. Two of the ticket's claims don't hold:crud.getSaveButton().setEnabled(true).Crud's internalSaveButtonoverridesonEnabledStateChangedto permanently override the web component's__isSaveBtnDisabled, and re-applies it on attach. Flow also overrides the client-side__validateto always pass, so the server-sideCrudEditor.validate()is the only gate on a save. Documented as supported, which makes the ADR-0020 pattern from the ticket achievable.getDeleteButton().setVisible(false). That isn't reliable:setVisible(false)sets Flow'shiddenattribute, but__deleteButtonPropsChangedrunstoggleAttribute('hidden', isNew)each time the editor opens, so Delete reappears when editing an existing item. Vaadin's own integration tests only usesetEnabledon these buttons. The docs recommendgetStyle().set("display", "none")instead, with a short note on why.CrudVariant.AURA_NO_BORDERis deprecated in favour ofNO_BORDERas of 25.2.1, so the docs point atNO_BORDER.Verification
mvn compilepasses.spotless:applyproduces no changes to the new Java files.crud-imports.ts..txtcopy against.vale-pr.iniand diffed against the pre-change baseline. The only new hits are artifacts of that workaround —[methodname]/[classname]roles,_italics_, and source-block attributes, all in ignored classes and scopes for the real run. The two genuine ones ("deletable" spelling, a Chicago title-case heading) are fixed.🤖 Generated with Claude Code
https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ