Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Related issue 

Jira
serverMagnolia - Issue tracker
columnskey,summary,type,created,updated,due,assignee,reporter,priority,status,resolution
serverId500b06a6-e204-3125-b989-2d75b973d05f
keyDEV-883

Table of Contents

Goal

We want to provide users with a simple, modern API to build arbitrarily complex fields.

Problem

All is good, as long as a simple value is bound to a simple field, e.g. TextField → String. 

...

Currently Magnolia does provide such ability through custom multi fields (see AbstractCustomMultiField) but they have several shortcomings, both in terms of internal implementation and from a client developer's perspective (see for instance 

Jira
serverMagnolia - Issue tracker
serverId500b06a6-e204-3125-b989-2d75b973d05f
keyMGNLUI-2542
).  

Proposal

  • Assume the field is simple and atomic

  • When you need to manage many related values - it is another form within a form

  • Complex fields are like forms but with stripped layout

Layouting 

Form (or editor) layout should be separated from the binding mechanism and should be flexible

...

PoC https://git.magnolia-cms.com/users/jsimak/repos/ui/pull-requests/1/commits/91980c4dbd0803b429e50f037f4b70a638cce0dd

A class diagram from the current PoC and a description of their main responsibilities

Image RemovedImage Added


FormDefinition

  • is a ComponentDefinition
  • is registered and mapped to a factory class like any other form field
  • has FieldDefinition(s)
  • has a LayoutDefinition



Code Pro
collapseTypeClassic
languageyaml
themeEmacs
titleForm configuration example (YAML)
# this is the 'main' form configuration
form:
  fields:
    title:
      class: info.magnolia.ui.form.poc.definition.TextFieldDefinition
    firstName:
      class: info.magnolia.ui.form.poc.definition.TextFieldDefinition
	lastName:
      class: info.magnolia.ui.form.poc.definition.TextFieldDefinition
    # address is a form component (a subform of the main form) and specifies its own layout.
    # It is declared under fields and we can refer to it as a field
    # see below form/layout/tabs/more/fields/address
    address:
      class: info.magnolia.ui.form.poc.definition.FormDefinition
      fields:
        city:
          class: info.magnolia.ui.form.poc.definition.TextFieldDefinition
        country:
          class: info.magnolia.ui.form.poc.definition.TextFieldDefinition
        nestedphone: # a form component can have sub-form components with their own layout
          class: info.magnolia.ui.form.poc.definition.FormDefinition
          fields:
            quxareaCode:
              class: info.magnolia.ui.form.poc.definition.TextFieldDefinition
  			number:
        layout:
            class: info.magnolia.ui.form.poc.layoutdefinition.MyLayoutTextFieldDefinition
            fieldslayout:
              qux:class: info.magnolia.ui.form.poc.layout.MyLayout	
      layout: # this is the layout for address
        class: info.magnolia.ui.form.poc.layout.DefaultLayoutDefinition
        fields:
          city:
          country:
          nested:
  # this is the main form's layout configuration
  # layout configurations could probably be omitted altogether and rely on a reasonable default 
  layout:
    class: info.magnolia.ui.form.poc.layout.TabbedLayoutDefinition
    tabs:
      personal:
        fields:
          title:
          firstName:
		  lastName:	
      more:
        fields:
          # address is a form component (a subform of the main form)
          # here it is referred to as if it were a plain Field
          address:


LayoutDefinition

has a list of field names composing the layout. Their names match those of the FieldDefinition(s)

  • specifies LayoutProducer implementation class


LayoutProducer

  • is instantiated from a LayoutDefinition
  • createLayout(LayoutDefinition, Map<String, Component>) creates the actual layout (a Vaadin Component) from
    • LayoutDefinition
    • a mapping of field names and actual field components

Form

  • is a plain Java class

  • during its building process it stores
    • its Binder
    • its own Layout (including sub-form layouts)
    • its sub-forms
  • can save and validate self and its sub-forms recursively (should this functionality move away from Form?) 

FormPresenter

  • is currently the orchestrator of the form building process
  • creates the final form view (a Vaadin Layout)
    • instantiates a Form from a FormDefinition
    • returns the form view to the caller

    • TODO 

    • set labels, help messages and descriptions: use something like info.magnolia.ui.dialog.formdialog.FormView?

    • update forms/fields based on Locale? See info.magnolia.ui.dialog.formdialog.FormPresenterImpl.updateForm()
  • it gets passed to DetailPresenter(..) which takes care of initializing actions and other stuff before calling and setting the form view produced by FormPresenter



Code Pro
languagejava
themeEmacs
titleSnippet from FormPresenter
collapsetrue
...
 /***
 * @return a {@link View} as described by this {@link FormDefinition} including nested forms, if any.
 */
public View createFormView(D formDefinition) {
    FormView view = new FormView();
    Form<T> form = formFactory.createForm(formDefinition);
    view.addComponent(form.getLayout());

    return view;
}


TODO

We now have the basic infrastructure to build and bind form components (including arbitrarily nested, complex sub-forms). Layout is split from field definitions. We can also read and write forms and sub-forms without the need for special transformers.

What is left to do. 

  • Form
    • styling
    • set labels, help messages and descriptions: use something like info.magnolia.ui.dialog.formdialog.FormView?

    • update forms/fields based on Locale? See info.magnolia.ui.dialog.formdialog.FormPresenterImpl.updateForm()

    • pass FormPresenter to DetailPresenter(..) which takes care of initializing actions and other stuff before calling and setting the form view?

  • Fields
    • implement them or harden the ones which we began to implement (TextField, SelectField)
    • populating select options based on the value of another field
    • validating a field depending on the value of another field (including within a composite field itself)
    • enabling/disabling fields conditionally
    • potentially custom handling of any field, via plain Vaadin code
    • properly highlighting validation on sub-fields (this should be already taken care of by Vaadin, only need proepr error field styling).
  • FormBinder/FormContext
    • currently saving a new item fails cause the node id we get is null.
    • current implementation relies on JcrNodeItemid and JcrNewNodeItemId: not sure we want to use them
  • Layout
    • naming
    • Custom layout impl doesn't work
    • provide a Vaadin declarative layout impl
    • In case of resource-based layout we could and should provide flt support (i.e. pre-process the script before binding to the fields)