Form items

Contents

In this chapter, you’ll create an Animal content type, give it alternative names and images, and link your artists to their favorite animals. You’ll then follow those relationships in Guillotine.

Form items are the building blocks of content forms. You’ve already used TextLine, TextArea and Date; now you’ll make a field repeatable and use selectors to reference other content.

Create the Animal form

An animal needs a species name and may have other names, such as "Caribou" for a reindeer. Use the display name for the species and a repeatable TextLine for its other names.

Task: Add alternative names

  1. Define alternative names - Create the Animal content type in your app:

    src/main/resources/cms/content-types/animal/animal.yaml
    kind: "ContentType"
    title: "Animal"
    description: "An animal that lives on planet Earth"
    displayNamePlaceholder: "Species"
    superType: "base:structured"
    form:
      - type: "TextLine"
        name: "otherNames"
        label: "Other names"
        helpText: "Other names for this species."
        occurrences:
          min: 0
          max: 0

As with Artist, displayNamePlaceholder tells editors what to enter in the display name, here the species. For the otherNames field:

  • type chooses the form item, in this case TextLine.

  • name is the property name used to store the value and expose it through GraphQL.

  • label and helpText guide the editor filling out the form.

  • occurrences allows the editor to add multiple values: min: 0 makes the field optional, and max: 0 means there is no upper limit.

Without occurrences, a field is optional and accepts a single value (min: 0, max: 1). Setting both to 1 makes it required. Content Studio allows editors to save drafts with missing required values, but those items cannot be marked as ready or published.

Choose field names that will last, and use camelCase, such as otherNames. Renaming a field does not migrate its stored values, and queries using its old name will need updating. You can change its label without changing the stored property or API field.

Select an image

The lion image you uploaded in the previous chapter is a separate content item. An ImageSelector stores a reference to it, so you can reuse the same image across several animals without uploading copies.

Task: Add an image selector

  1. Add image references - Append this form item to the Animal form, at the same indentation as otherNames:

    src/main/resources/cms/content-types/animal/animal.yaml
      - type: "ImageSelector"
        name: "images"
        label: "Images"
        helpText: "Images of the animal"
        occurrences:
          min: 0
          max: 3

Here, min: 0 and max: 3 allow up to three images, including none. ImageSelector lets editors select existing images or upload new ones. For this exercise, you’ll select the existing lion image.

Task: Create two animals

  1. Deploy the Animal schema - Save the schema file and let your running enonic dev watcher update the app.

  2. Create the Animals folder - In Content Studio, create a folder named "Animals" at the root of "My First Project", next to Artists and Images. Check that its path is /animals.

  3. Create Lion - Select Animals, click New, and choose Animal. Enter "Lion" as the display name and select your uploaded lion.jpg in the Images field. Leave Other names empty and save the item.

    Animal form for Lion with an empty Other names field and the selected lion image
    Figure 1. The Animal form with a reference to the uploaded lion image
  4. Create Reindeer - Create another Animal named "Reindeer" in the same folder. Enter "Caribou" in Other names, leave Images empty, and save it. Notice that you can add another Other names entry because the field is repeatable.

Both items are saved in Draft; keep using Draft in Query Playground for the exercises below.

Query the animal’s fields

Guillotine uses occurrences when generating the GraphQL fields. Both otherNames and images accept multiple values, so both become lists, even when an item has just one value. The names are strings; the images are content references whose fields you can select.

Task: Fetch the lion and its image

  1. Fetch Lion and its image - Open Query Playground for "My First Project", keep Draft selected, and run:

    {
      guillotine {
        get(key: "/animals/lion") {
          displayName
          ... on com_example_dev101_Animal {
            data {
              otherNames
              images {
                displayName
                ... on media_Image {
                  imageUrl(scale: "width(600)") {
                    path
                    queryString
                  }
                }
              }
            }
          }
        }
      }
    }

    The result contains an empty otherNames array and one entry in images. Build the image URL by joining http://localhost:8080/api, path and queryString, as in the media chapter.

  2. Compare Reindeer - Change the key to /animals/reindeer and run the query again. The otherNames list now contains "Caribou", while images is empty.

The get result and the items in images use GraphQL’s Content interface. As in the Headless API chapter, an inline fragment selects fields belonging to a more specific GraphQL type:

  • ... on {app-name-query}_Animal gives you access to the Animal’s data.

  • ... on media_Image gives you access to the image’s imageUrl.

These are GraphQL type names; your CMS content types are com.example.dev101:animal and media:image. The image selector stores the image’s ID, and Guillotine resolves that reference for you.

A ContentSelector can reference other kinds of content, just as ImageSelector references images. You’ll use it to give each artist one optional favorite animal.

Task: Add a favorite animal

  1. Define the favorite animal field - Append this field to the Artist form, after birthday:

    src/main/resources/cms/content-types/artist/artist.yaml
      - type: "ContentSelector"
        name: "favoriteAnimal"
        label: "Favorite animal"
        allowContentType:
          - "animal"
        allowPath:
          - "/animals/*"
  2. Select each artist’s favorite animal - Save the schema, let the watcher update the app, and reopen your artists in Content Studio. Make these selections and save each artist:

    Artist Favorite animal

    Cardi B

    Lion

    Missy Elliott

    Reindeer

    P!nk

    Lion

    Artist form for Missy Elliott with Reindeer selected as her favorite animal
    Figure 2. The Artist form with an animal selected

The selector narrows the available choices in two ways:

  • allowContentType permits only the animal content type from this app.

  • allowPath restricts the choices to content below /animals.

There is no occurrences setting, so the editor can select at most one animal and can leave the field empty.

Reference an item when it should be edited and reused independently. Two artists can reference the same Lion, and an edit to that animal is visible through both references. For data that only belongs to one item, such as an artist’s filmography entries, you’ll use sets in the next chapter.

Task: Follow the reference in a query

  1. Fetch the referenced animals - Start with the type-filtered artist query from the Headless API chapter and select favoriteAnimal:

    {
      guillotine {
        queryDsl(
          query: {
            term: {
              field: "type"
              value: { string: "com.example.dev101:artist" }
            }
          }
        ) {
          displayName
          ... on com_example_dev101_Artist {
            data {
              favoriteAnimal {
                displayName
                ... on com_example_dev101_Animal {
                  data {
                    otherNames
                  }
                }
              }
            }
          }
        }
      }
    }

The favoriteAnimal field returns a single content item. You can read its displayName directly, then use an Animal fragment to select data.otherNames. There is no need for a separate query to fetch the referenced animal.

With the sample selections above, the data.guillotine.queryDsl array contains the following items; their order may differ:

[
  {
    "displayName": "Cardi B",
    "data": {
      "favoriteAnimal": {
        "displayName": "Lion",
        "data": {
          "otherNames": []
        }
      }
    }
  },
  {
    "displayName": "Missy Elliott",
    "data": {
      "favoriteAnimal": {
        "displayName": "Reindeer",
        "data": {
          "otherNames": ["Caribou"]
        }
      }
    }
  },
  {
    "displayName": "P!nk",
    "data": {
      "favoriteAnimal": {
        "displayName": "Lion",
        "data": {
          "otherNames": []
        }
      }
    }
  }
]

If an artist has no selection, favoriteAnimal is null. Compare that with the empty lists for an animal’s unused otherNames or images fields.

More form items

You now have the basics for configuring fields and linking content. The CMS form items reference covers the full range of form items and their settings, including ImageSelector and ContentSelector.

For a file that belongs directly to one content item, AttachmentUploader stores it as an attachment on that item, instead of creating separate media content to reference. Query the content item’s attachments field and select attachmentUrl on each attachment to retrieve its URL components. See the Guillotine Attachment reference for the available fields.

Coming up

You’ve created repeatable fields, selected an image, linked artists to animals, and followed those references through Guillotine. In the next chapter, you’ll group and reuse form items with sets and form fragments.


Contents

Contents