Goal

Learn about some of the different attribute element types that can be configured on a form and their behaviour.

Starting code

Start tag: lesson/02

Requirements

We want to make the following changes to the "Register a Super Hero" form content (note: make all fields mandatory unless otherwise stated).

  1. Ensure the Name field requires a minimum of two characters, with an error message of “Mononyms are fine, but a single letter is pushing it a little”
  2. Add a “Registration Date” date-only field, with a value of the date the form was started that cannot be edited
  3. Add an optional “Year of arrival on Earth” that only accepts numbers and up to four digits long with hint text “Can be up to four digits”
  4. Add a “Do you have a uniform, costume or outfit?” field with a radio option with values “Yes” and “No” with “Yes” selected by default. Also include the following help text “It is recommended heroes have a uniform or costume that obscures their identity for privacy reasons.”
  5. If they answer “Yes” to the uniform question, display an additional “Do you usually or intend to don a cape?” field with a radio option with values “Yes” and “No”
  6. Add a “Hero Powers” field where you can type ahead and select up to five from the following super powers. Have the text “Start typing for options…” as a placeholder in the input field when nothing is entered.
    • Strength
    • Speed
    • Flight
    • Invulnerability
    • Invisibility
    • Heightened senses
    • Fighting skill
    • Weaponry
    • Agility
    • Other powerful stuff

Steps

Attribute Properties

Making an attribute mandatory, read-only or invisible is simply a matter of adding the following relevant attribute with the appropriate value:

  • mandatory – “true” if the attribute is required, “false” (the default) if it is optional
  • readOnly – “true” if the attribute should not be editable on an otherwise editable form, “false” (the default) if it is editable
  • visible – “true” (the default) if the attribute should appear on the form, “false” if it should be hidden

Name Field

Add a mandatory attribute with a value of "true" to the existing Name attribute element on the "heroRegister" form. This handles the basic mandatory requirement, but we’ll also need to add the following child element to the attribute element to ensure the minimum two character validation is applied:

<platform:validate-length min-length="2"/>

Restart the service to ensure the validation is triggered when only a single character is entered and then focus is removed from the field (e.g. by pressing the Tab).

So the validation is triggered, but the message isn’t quite what we’re after, so add an error-code-too-short attribute with a value of "hero.name.tooShort" and a text key for this in the heroes en.xml file with the appropriate value "Mononyms are fine, but a single letter is pushing it a little". When done the Name attribute in the "heroRegister" form should look like this:

<attribute attribute="Name" textKeyPrefix="hero.name" mandatory="true">
  <platform:validate-length min-length="2" error-code-too-short="hero.name.tooShort"/>
</attribute>

Registration Date field

Next we’ll add the "Registration Date" attribute field. This is much like the Name attribute but this time we’ll use an attribute name of RegistrationDate, and we’ll give it a textKeyPrefix attribute with a value of "hero.registrationDate". There are three other differences with this attribute compared to the Name one: firstly, it takes a date value, second its value is defaulted to today, and lastly it’s read-only.

The read-only state is satisfied simply using the readOnly attribute property mentioned above. Use another platform tag <platform:set-attribute-value.../> to handle giving it the initial default value, and we’ll use a data constraint for the first time in the training application to restrict the value to a date-only value.

Data constraints are primarily used to restrict the values, or type of value an attribute can be. Like text keys, data constraints are defined separately, then referenced in attribute elements using the dataConstraint attribute. The "dateOnly" and "dateTime" data constraint are used for date attributes, with the former being for days only and the latter allowing a time of day also, and these are already defined within the platform, so can simply be referenced by the attribute element by adding dataConstraint="dateOnly" within its opening tag.

Have a go at adding these things first, making use of Magellan’s code completion mechanism via Ctrl+space (⌘+space) before comparing your configuration to that shown below for the RegistrationDate attribute. Also re-load in the "Register a Super Hero" service via the application to check everything’s looking OK (hint: don’t forget the to add the "hero.registrationDate.label" text key for its label!).

<attribute attribute="RegistrationDate" dataConstraint="dateOnly" textKeyPrefix="hero.registrationDate" readOnly="true"> 
  <platform:set-attribute-value scope="instantiate" value-expression="new Date()" when-attribute-value-empty="true"/>
</attribute>

Year of Arrival Field

This field is restricted to digits only, up to four of them, reflected by the required hint text of "Can be up to four digits". It’s optional so we can exclude the mandatory="true" attribute too.

We’ll use another pre-defined data constraint to restrict it to digits, this time the "long" data constraint. We’ll call this attribute YearOfArrival (we’ll assume the registry will be hosted on Earth so is implicit ;)) and we’ll use a textKeyPrefix of "hero.yearOfArrival" and this time we’ll need to define two text keys – the usual one ending in ".label" for the label, plus another one ending in ".hint" for the hint text.

Start by adding this, the dataConstraint="long", textKeyPrefix="hero.yearOfArrival" and the label and hint text keys before starting a "Register a Super Hero" service again to check the changes in the application.

Notice how you can only type digits into this field? We’ll now add the maximum of four digits validation via the same <platform:validate-length.../> tag we used for the Name attribute earlier, but this time using the max-length option instead and we’ll just keep the default error message.

Once done try it out in the application again and you might notice that you don’t get the validation error… in fact, you can’t – the UI is restricted so you can’t even add more than the maximum four digits. Mind blown ? Just to confirm the validation error would kick in if we could though, relax the UI restriction using either the "ui-restrict" and/or "ui-max-length" options and check you then get an error when you add too many digits. Nice one.

Once done, check your newly configured attribute to the XML below and ensure it works as expected via the application.

<attribute attribute="YearOfArrival" dataConstraint="long" textKeyPrefix="hero.yearOfArrival"> 
  <platform:validate-length max-length="4"/>
</attribute>

Costume Field

The "Do you have a uniform, costume or outfit?" field introduces another well-worn standard data constraint, the "yesNo" data constraint that allows for simple binary yes/no type questions. The convention when using one of these field is to end with the suffix "Yn" (although not a requirement) so we’ll call this attribute CostumeYn. The allowed values for "yesNo" attributes are either "Y" or "N" (hopefully it’s obvious what these values represent). The requirement for this field is it has the "Yes" option selected by default, which is "Y" in terms of the actual raw attribute value and since this is a simple, literal value we can set this using the defaultValue attribute. We’ll keep with convention and use a textKeyPrefix value of "hero.costumeYn" and like the field in the section above, two text keys will need to be defined for this field, the standard label one and this time a help one using the ".help" suffix and value "It is recommended heroes have a uniform or costume that obscures their identity for privacy reasons.". As usual, give this a go on your own using Magellan’s code completion (Ctrl+space), navigation and resource generation before comparing your answer to the configuration below.

<attribute attribute="CostumeYn" dataConstraint="yesNo" textKeyPrefix="hero.costumeYn" mandatory="true" defaultValue="Y"/>

Once again, check the field in the application via a fresh "Register a Super Hero" service, noticing in particular that the default editable display for an attribute with a "yesNo" constraint are radio buttons.

Cape Field (and Group Selectors)

Onto the "Do you usually or intend to don a cape?" field, which in itself is another simple Yes/No question, but differs in that it should only appear if the answer to the previous uniform question was "Yes". This just so happens to be a perfect example to introduce the concept of Group Selectors.

Group selectors involve a small amount of configuration both on the attribute element (the "source") that triggers the visibility of other sibling elements of the form (you can target any service form item (SFI) not just attributes), plus the elements on the form who’s visibility you want to control (the "target(s)"). This means we have to update the CostumeYn attribute element we’ve just added in the section above, firstly adding the all important groupSelector="true" to the opening tag to mark it as a group selector, as well as adding selectOption child elements to specify some control group selector groups. The final result of the CostumeYn attribute after these changes are now:

<attribute attribute="CostumeYn" dataConstraint="yesNo" groupSelector="true" textKeyPrefix="hero.costumeYn" mandatory="true" defaultValue="Y"> <selectOption group="costumeY" value="Y"/> <selectOption group="costumeN" value="N"/></attribute>

Hopefully they’re relatively self-explanatory, they define two "groups" one called "costumeY" and the other called "costumeN". The "costumeY" group is "active" when the attribute has a value of "Y" and, you guessed it, the "costumeN" group is active when the attribute has a value of "N". We can then tag other sibling elements on the form sections of the form with these groups to include or exclude them based on the group being active.

Before we configure the target element with the relevant group selector group, let’s first see what it would look like if it was a standard field that was always visible independent of any other fields. There isn’t anything new in this case, so try to configure it yourself using an attribute name of CapeYn and the usual conventions before checking the result against the XML below:

<attribute attribute="CapeYn" dataConstraint="yesNo" textKeyPrefix="hero.capeYn" mandatory="true"/>

Now that we’ve got this, it’s super simple to associate it to the "customeY" group selector group by adding a <group group="costumeY"/> child element to it, so it’s only present if the appropriate answer is given in the field above. So the full attribute configuration will now look like:

<attribute attribute="CapeYn" dataConstraint="yesNo" textKeyPrefix="hero.capeYn" mandatory="true">
  <group group="costumeY"/>
</attribute>

How easy was that? Check the application to get a good feel on how the group selector works, controlling the presence of the cape question based on the answer to the costume one. Super cool 🙂

Powers Field

The final attribute to be added in this lesson is the "Hero Powers" one. It’s also the most fancy 🙂 We’ll call this attribute SuperPowers and they can be up to five of a static list of powers. Whenever you see a static list of values, think data constraint. So we’ll use a dataConstraint attribute like we have with other field in this lesson already, but unlike the dateOnly and yesNo data constraints, this one won’t exist as it isn’t standard so we’ll have to create the data constraint itself as well, rather than just reference it from the attribute element. The data constraint can be called anything we want as long as it doesn’t unexpectedly clash with another data constraint within the module or application, and we’ll call it "superPowers". So add the SuperPowers attribute to the form as usual, making it mandaory, giving it a textKeyPrefix of "hero.superPowers" and with a dataConstraint value of "heroPowers", all which should give you the configuration below.

<attribute attribute="SuperPowers" dataConstraint="heroPowers" textKeyPrefix="hero.superPowers" mandatory="true"/>

Add the related text keys to the heroes en.xml – a "hero.superPowers.label" one with a value of "Hero Powers" and a "hero.superPowers.placeholder" one with a value of "Start typing for options…" for the field input placeholder text. Now we’re ready to add our new data constraint, so back on the SuperPowers attribute element on the "heroRegister" form, right-click on the "superPowers" data constraint value and select Create Resource. There’s nothing further to add within the dialog so just select Create and you’ll be in the dataConstraints.xml with the bare bones of a new "superPowers" data constraint as per the Fig 2.1 below.

Fig 2.1 A newly created “superPowers” data constraint resource.

So let’s go ahead and complete the data constraint. Firstly, we’re dealing with values of of type "string", so that’s the type attribute value to use. Next it is restricted as we want only the values we specify in the constraint to be available, so set the restrict attribute to "true". In this case we also want to allow multiples as our heroes can have more than one power, so set the allowMultiple attribute to "true" also.

It’s worth mentioning here that when adding multiple values for an attribute whether we should store them within a single attribute (as a comma separated value (CSV)) or have each value as its own record instead. We haven’t gotten to records and repeater configuration in our training yet, so we won’t go into the alternative option for now, suffice to say it’s something you should consider when configuring a multi-valued field. Deciding factors are whether you need any additional data e.g. a code plus another value, a start and/or end date, etc. and be sure to include how the data can be maintained when considering.

Now we just need to add the required values as child elements. Use basic camelCase values for the code attribute and let the text key relating to the textKey attribute value have the full textual values. Once completed you should have something like the following configuration (plus the related text keys defined with the relevant full textual values).

<dataConstraint name="heroPowers" type="string" restrict="true" allowMultiple="true"> <value code="strength" textKey="hero.power.strength"/>
  <value code="speed" textKey="hero.power.speed"/>
  <value code="flight" textKey="hero.power.flight"/>
  <value code="invulnerability" textKey="hero.power.invulnerability"/>
  <value code="invisibility" textKey="hero.power.invisibility"/>
  <value code="senses" textKey="hero.power.senses"/>
  <value code="fighting" textKey="hero.power.fighting"/>
  <value code="weapons" textKey="hero.power.weapons"/> 
  <value code="agility" textKey="hero.power.agility"/> 
  <value code="other" textKey="hero.power.other"/>
</dataConstraint>

If we check the application at this point we’ll see there’s a drop down select box for the "Hero Powers" field (which is the default for restricted data constraints of type "string" or "long") but there’s still no way to add multiple values or start typing to filter values as the placeholder text "Start typing for options…" suggests. For this last step we need to take a closer look at the UI options available. The UI multi-select documentation refers to the UI pillbox as a means of rendering a multi-select attribute with type-ahead filtering, which sounds like exactly what we’re after. What are the chances? 🙂 Look at the UI pillbox documentation to ensure you understand the configuration options we’ve added to the make sense of the options we’ve updated the SuperPowers attribute element with below (i.e. the widget attribute and the "ui-" prefixed value child elements).

<attribute attribute="SuperPowers" dataConstraint="heroPowers" widget="pillbox" textKeyPrefix="hero.superPowers" mandatory="true"> 
  <value name="ui-max" value="5"/>
</attribute>

Wow, what a difference eh? Notice how you can now type to filter plus only add up to five powers as per the requirement via the UI.

For extra credit, play around with different values of “ui-max” as well as other values mentioned in the UI pillbox documentation (e.g. “ui-min-length“) and see how they affect the “Hero Powers” field in the UI.

It looks like we’re all done and all of the requirements for this field are now met, but before we rush on, let’s first consider how we’ve limited the number of hero powers to a maximum of five. We did it using the "ui-max" value, right? And this stops us being able to add more than five in the UI. But that’s just client-side, where it’s possible for things to be bypassed (e.g. using CSS, JS) that may remove this UI restriction. What we really want is some server side validation. If you’re still not convinced, what if this same configuration was used on a web service where the UI doesn’t come into play? Sure, it’s not our concern right now, but we create world class registries where security is paramount and business rules are often backed by legislation and where simply "good enough" isn’t. So let’s add some server side protection now (which actually adds the same UI restriction too by default). This can be done using the <platform:validate-multiple-values .../> child tag, so go ahead and add this to try to limit the number of powers to five. Also disable the UI restriction briefly using the "ui-restrict" option to confirm you get a validation error if you add six. Great – totally worth that extra security server side validation provides so we can all sleep peacefully at night, right?

The full SuperPowers attribute element should now look like the following.

<attribute attribute="SuperPowers" dataConstraint="heroPowers" widget="pillbox" textKeyPrefix="hero.superPowers" mandatory="true"> 
  <platform:validate-multiple-values max-count="5"/>
</attribute>

Now we’re done. Good job.

Add tests

We are going to create a new Happy Path scenario to cover 5 new scenarios for the options:

  • Do you have a uniform, costume or outfit?
  • Do you usually or intend to don a cape?

Also we are adding validations for help text and select different options for the Hero powers and validate dates

This can be done by following the steps outlined in lesson 2 of the test training.

Wrap up

Attributes, attributes, attributes – that was the name of the game for this lesson. We looked at how to configure the following different types of attribute elements which you’re now hopefully comfortable with:

  • Setting simple attribute element properties and their defaults when not explicitly set – mandatory, readOnly and visible.
  • Additional validation applied using platform tags such as platform:validate-length and platform:validate-multiple-values.
  • Initialising an attribute element’s value, both using the defaultValue attribute for simple types (i.e. strings) and expressions in the platform:set-attribute-value tag for more complex types such as Groovy dates.
  • Data constraints – different types (i.e. dateOnly, dateTime, string, yesNo, long, etc.), standard ones built into Verne as well as adding new custom ones.
  • Group selectors and how these can be used to make parts of the form active or not based on an attribute’s value.
  • Multi-valued attribute elements and the options available in the UI including the pillbox for supporting multiple selections and type ahead filtering.

Extra credit

Try making the changes mentioned below to further hone your skills covered in this lesson:

  • Play around with some of the attribute element opening tag attribute values, as well as the child element platform tags and look to introduce others (using the Ctrl+space shortcut to see configuration options, as well as Ctrl+space+space for documentation) and see what impact they have on the form display or validation.
  • Put the CapeYn attribute element inside a box element and move the group element to the box to check Group Selector targets can be any sibling element, not just attributes (even though the Group Selector source must be an attribute element).
  • Also add the same group element to another attribute element to see the same Group Selector working on multiple targets. What about if you have one group element using the “costumeY” group and the other the “costumeN” group?
0
0

Jump to Section