Edit a list field as a table#
This guide shows you how to make a field holding an ordered list render in Volto as a table: one row per entry, a handle to drag it by, and a dialog to edit it.
@plone-collective/volto-identity supplies the widgets, and your field asks for
one by name. The example running through this guide is the field that uses one
already: a Profile's emails.
1. Pick the widget#
The field holds |
Widget |
An entry is edited with |
|---|---|---|
Strings, in a |
|
The widget of the list's |
Objects, in a |
|
An item schema registered in the frontend |
2. Ask for it from the field#
Name the widget in frontendOptions. This is the emails field:
from plone.autoform.directives import widget
from plone.schema import Email
from plone.supermodel import model
from zope import schema
class IEmailAddresses(model.Schema):
emails = schema.Tuple(
title=_("Email addresses"),
value_type=Email(title=_("Email")),
required=True,
missing_value=(),
default=(),
)
widget("emails", frontendOptions={"widget": "identity_string_list"})
The column header and the one field in the dialog both come from value_type.
Here the header is Email, and the dialog uses Volto's email widget, validation
included. plone.restapi serves a Tuple with uniqueItems, so the widget
refuses an entry already on the list. It serves a List of text without it, and
the widget accepts a repeated entry there.
For a list of objects, also pass the item schema's name, and the columns to show,
in widgetProps:
from plone.autoform import directives
from plone.schema import JSONField
from plone.supermodel import model
import json
class ILinks(model.Schema):
directives.widget(
"links",
frontendOptions={
"widget": "identity_object_list",
"widgetProps": {"schemaName": "links", "columns": ["id", "title"]},
},
)
links = JSONField(
title=_("Links"),
schema=json.dumps({"type": "array", "items": {"type": "object"}}),
default=[],
required=False,
)
plone.restapi serves frontendOptions as written, and Volto hands
widgetProps to the widget as props. Leave out columns and every field of the
item schema is a column.
3. Describe an entry#
A list of strings skips this step.
For a list of objects, register the item schema in your project's frontend
configuration, under the name schemaName gives:
config.registerUtility({
name: 'links',
type: 'schema',
method: () => ({
title: 'Link',
fieldsets: [
{ id: 'default', title: 'Default', fields: ['id', 'title', 'href'] },
],
properties: {
id: {
title: 'Network',
choices: [
['github', 'GitHub'],
['website', 'Website'],
],
},
title: { title: 'Title' },
href: { title: 'Target' },
},
required: ['id', 'title', 'href'],
}),
});
title names one entry, so the add button here is labelled Add Link.
The method receives the props of the widget, and props and intl as keys as
well, so it can translate its titles with intl.formatMessage.
Volto's own object_list widget finds its schema through schemaName too, so a
field written for that widget needs no new utility.
Verify#
Open the edit form of an object with the field. The field is a table with a row per entry, under a header naming the columns.
Dragging a row by its handle moves it.
Edit opens the entry in a dialog.
Delete asks before it removes the row.
The button beside the field's label opens an empty dialog for a new entry.
Nothing is stored until you save the form, so Cancel on the form undoes all of it.