Skip to content
Backlit
Esc
navigateopen⌘Jpreview
On this page

Group field

Display multiple record keys as one composite value.

A group field combines multiple fields into one displayed property. Use it for an address, a person name, or another value that is stored in separate record keys but reads as one item.

Grouping happens when you compose the view. The raw record stays flat. The SDK remaps the child values and puts them under the group name in the protocol data.

API

fields.group<Name extends string>(
  name: Name,
  fields: Nodeable<Field>[],
  options?: GroupFieldOptions
): GroupFieldBuilder<Name>
Option Input Result
label string Sets the text shown for the group.
description string Adds supporting text for the group label.

configure(options) returns a configured clone. clone() returns an independent copy with the same child fields and options. configure cannot replace the child fields because that would define a different group.

The child field order controls the value order used by the renderer.

Basic group field

Declare the raw fields on the resource. Create the group when you select fields for the datalist.

import {
  datalist,
  defineResource,
  fields,
} from '@backlit/sdk'

const orders = defineResource('orders', [
  fields.text('line1'),
  fields.text('city'),
  fields.text('postcode'),
])

orders.view('detail', () =>
  datalist({
    line1: '12 Market Street',
    city: 'London',
    postcode: 'SW1A 1AA',
  }).setFields([
    fields
      .group(
        'address',
        orders.pick('line1', 'city', 'postcode'),
        { label: 'Address' }
      ),
  ])
)

export default orders
{
  "kind": "success",
  "status": 200,
  "node": {
    "kind": "view",
    "resource": "orders",
    "name": "detail",
    "slots": {
      "content": [
        {
          "kind": "datalist",
          "fields": [
            {
              "kind": "group",
              "name": "address",
              "label": "Address",
              "fields": [
                {
                  "kind": "text",
                  "name": "line1",
                  "label": ""
                },
                {
                  "kind": "text",
                  "name": "city",
                  "label": ""
                },
                {
                  "kind": "text",
                  "name": "postcode",
                  "label": ""
                }
              ]
            }
          ],
          "data": {
            "address": {
              "line1": "12 Market Street",
              "city": "London",
              "postcode": "SW1A 1AA"
            }
          }
        }
      ]
    }
  }
}

Description

The group description applies to the complete composite value.

import {
  datalist,
  defineResource,
  fields,
} from '@backlit/sdk'

const orders = defineResource('orders', [
  fields.text('line1'),
  fields.text('city'),
])

orders.view('detail', () =>
  datalist({
    line1: '12 Market Street',
    city: 'London',
  }).setFields([
    fields.group('address', orders.pick('line1', 'city'), {
      label: 'Address',
      description:
        'The address printed on the shipping label.',
    }),
  ])
)

export default orders
{
  "kind": "success",
  "status": 200,
  "node": {
    "kind": "view",
    "resource": "orders",
    "name": "detail",
    "slots": {
      "content": [
        {
          "kind": "datalist",
          "fields": [
            {
              "kind": "group",
              "name": "address",
              "label": "Address",
              "description": "The address printed on the shipping label.",
              "fields": [
                {
                  "kind": "text",
                  "name": "line1",
                  "label": ""
                },
                {
                  "kind": "text",
                  "name": "city",
                  "label": ""
                }
              ]
            }
          ],
          "data": {
            "address": {
              "line1": "12 Market Street",
              "city": "London"
            }
          }
        }
      ]
    }
  }
}

Child fields

Each child keeps its field kind and presentation options. The SDK remaps every child value before it creates the grouped protocol data.

import {
  datalist,
  defineResource,
  fields,
} from '@backlit/sdk'

const orders = defineResource('orders', [
  fields.text('line1'),
  fields.text('city'),
  fields.number('postcode', { format: 'integer' }),
])

orders.view('detail', () =>
  datalist({
    line1: '12 Market Street',
    city: 'London',
    postcode: '10001',
  }).setFields([
    fields
      .group(
        'address',
        orders.pick('line1', 'city', 'postcode'),
        { label: 'Address' }
      ),
  ])
)

export default orders
{
  "kind": "success",
  "status": 200,
  "node": {
    "kind": "view",
    "resource": "orders",
    "name": "detail",
    "slots": {
      "content": [
        {
          "kind": "datalist",
          "fields": [
            {
              "kind": "group",
              "name": "address",
              "label": "Address",
              "fields": [
                {
                  "kind": "text",
                  "name": "line1",
                  "label": ""
                },
                {
                  "kind": "text",
                  "name": "city",
                  "label": ""
                },
                {
                  "kind": "number",
                  "name": "postcode",
                  "format": "integer",
                  "label": ""
                }
              ]
            }
          ],
          "data": {
            "address": {
              "line1": "12 Market Street",
              "city": "London",
              "postcode": 10001
            }
          }
        }
      ]
    }
  }
}

Nested group

A group can contain another group. The protocol keeps the same nested field and data structure.

import {
  datalist,
  defineResource,
  fields,
} from '@backlit/sdk'

const orders = defineResource('orders', [
  fields.text('recipient'),
  fields.text('line1'),
  fields.text('city'),
])

orders.view('detail', () =>
  datalist({
    recipient: 'A. Customer',
    line1: '12 Market Street',
    city: 'London',
  }).setFields([
    fields
      .group(
        'shipping',
        [
          ...orders.pick('recipient'),
          fields.group(
            'address',
            orders.pick('line1', 'city')
          ),
        ],
        { label: 'Shipping' }
      ),
  ])
)

export default orders
{
  "kind": "success",
  "status": 200,
  "node": {
    "kind": "view",
    "resource": "orders",
    "name": "detail",
    "slots": {
      "content": [
        {
          "kind": "datalist",
          "fields": [
            {
              "kind": "group",
              "name": "shipping",
              "label": "Shipping",
              "fields": [
                {
                  "kind": "text",
                  "name": "recipient",
                  "label": ""
                },
                {
                  "kind": "group",
                  "name": "address",
                  "label": "",
                  "fields": [
                    {
                      "kind": "text",
                      "name": "line1",
                      "label": ""
                    },
                    {
                      "kind": "text",
                      "name": "city",
                      "label": ""
                    }
                  ]
                }
              ]
            }
          ],
          "data": {
            "shipping": {
              "recipient": "A. Customer",
              "address": {
                "line1": "12 Market Street",
                "city": "London"
              }
            }
          }
        }
      ]
    }
  }
}

Clone

Use configure to reuse the child field set with different group options. The original group does not change.

import {
  datalist,
  defineResource,
  fields,
} from '@backlit/sdk'

const address = fields.group(
  'address',
  [fields.text('line1'), fields.text('city')],
  { label: 'Address' }
)

const orders = defineResource('orders', [])

orders.view('detail', () =>
  datalist({
    line1: '12 Market Street',
    city: 'London',
  }).setFields([
    address.configure({ label: 'Shipping address' }),
  ])
)

export default orders
{
  "kind": "success",
  "status": 200,
  "node": {
    "kind": "view",
    "resource": "orders",
    "name": "detail",
    "slots": {
      "content": [
        {
          "kind": "datalist",
          "fields": [
            {
              "kind": "group",
              "name": "address",
              "label": "Shipping address",
              "fields": [
                {
                  "kind": "text",
                  "name": "line1",
                  "label": ""
                },
                {
                  "kind": "text",
                  "name": "city",
                  "label": ""
                }
              ]
            }
          ],
          "data": {
            "address": {
              "line1": "12 Market Street",
              "city": "London"
            }
          }
        }
      ]
    }
  }
}

All options

You can use a label, description, child fields, and Panel composition with the same group.

import {
  datalist,
  defineResource,
  fields,
  panel,
} from '@backlit/sdk'

const orders = defineResource('orders', [
  fields.text('line1'),
  fields.text('city'),
  fields.text('postcode'),
])

orders.view('detail', () =>
  panel([
    datalist({
      line1: '12 Market Street',
      city: 'London',
      postcode: 'SW1A 1AA',
    }).setFields([
      fields
        .group(
          'address',
          orders.pick('line1', 'city', 'postcode'),
          {
            label: 'Shipping address',
            description:
              'The address printed on the shipping label.',
          }
        ),
    ]),
  ]).setTitle('Delivery')
)

export default orders
{
  "kind": "success",
  "status": 200,
  "node": {
    "kind": "view",
    "resource": "orders",
    "name": "detail",
    "slots": {
      "content": [
        {
          "kind": "panel",
          "title": "Delivery",
          "slots": {
            "content": [
              {
                "kind": "datalist",
                "fields": [
                  {
                    "kind": "group",
                    "name": "address",
                    "label": "Shipping address",
                    "description": "The address printed on the shipping label.",
                    "fields": [
                      {
                        "kind": "text",
                        "name": "line1",
                        "label": ""
                      },
                      {
                        "kind": "text",
                        "name": "city",
                        "label": ""
                      },
                      {
                        "kind": "text",
                        "name": "postcode",
                        "label": ""
                      }
                    ]
                  }
                ],
                "data": {
                  "address": {
                    "line1": "12 Market Street",
                    "city": "London",
                    "postcode": "SW1A 1AA"
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Form input

The input role uses a nested object at the group name. For an address group, seed address: { city, country }; the submitted payload has the same structure. Condition paths include the group name, such as address.country. Lookup arguments in the group must name members of that group.

resource.group(fields) creates an anonymous group for a relation display. Use a named fields.group when the group must occupy a record key in the output.

Was this page helpful?