Drop Downs and Defaults
Invoking an action whose parameters are primitives or values (int, date, string etc) is simple: the user can just type in or use a date picker.
Invoking an action with a parameter of reference type (such as Customer
or Order
) requires the viewer to provide some mechanism by which the end-user can select the relevant instance.
If the list of available options is fixed then the developer can provided a list a choices…() supporting method (for either and action parameter or when editing a property). These are rendered in a drop-down.
If the list of available options is much larger, then the developer can use an autoComplete…() supporting method. The user user enters a few characters and this is used to search for matching reference(s), again rendered in a drop-down.
Similarly, when invoking an action, there may well be suitable defaults for the action arguments.
For example, if placing an Order
then — even if the Product
argument might not have a sensible default — the quantity argument could reasonably be defaulted to 1.
Or, the Product
might indeed have a default, say the product previously placed by this user.
The developer indicates this using a default…() supporting method.
Choices and Default
For example, choices for a property are specified using:
import lombok.Getter;
import lombok.Setter;
@Property(editing = Editing.ENABLED) (1)
@Getter @Setter
private String paymentMethod;
public List<String> choicesPaymentMethod() { (2)
return Arrays.asList("Visa", "Mastercard", "Amex");
}
1 | If required; properties are by default disabled globally. |
2 | Note the "choices" prefix and the suffix matching up with the getter. The method must return a collection of the same type as the property. |
For an action the choices and a suitable default method would be:
public Order changePaymentMethod(String paymentMethod) {
setPaymentMethod(paymentMethod);
return this;
}
public List<String> choices0ChangePaymentMethod() { (1)
return Arrays.asList("Visa", "Mastercard", "Amex");
}
public String default0ChangePaymentMethod() { (2)
return getPaymentMethod(); (3)
}
1 | "choices" prefix, N-th param, suffix matches up with the action’s name. Returns a collection of the same type as the parameter. |
2 | "default" prefix, N-th param, the suffix matches up with the action’s name. Returns object of same type as parameter. |
3 | Common idiom to return the current value of an property of the object. |
AutoComplete
The autocomplete is similar to choices, but accepts a string parameter, to search for matching results. A property for example might have:
@Property(editing = Editing.ENABLED) (1)
@Getter @Setter
private Product product;
public List<Product> autoCompleteProduct( (2)
@MinLength(2) String search) { (3)
return productRepository.findByReferenceOrName(search);
}
1 | If required; properties are by default disabled globally. |
2 | "autoComplete" prefix, suffix matches property name. Returns a collection of the property’s type. |
3 | The @MinLength(…) annotation indicates the minimum number of characters that must be entered before a search is initiated. |
Actions are very similar:
public Order changeProduct(Product product) {
setProduct(product);
return this;
}
public List<Product> autoComplete0Product( (1)
@MinLength(2) String search) {
return productRepository.findByReferenceOrName(search);
}
1 | "autoComplete" prefix, N-th param, suffix matches action name. Returns a collection of the parameters type. |
An autoComplete method can be used in conjunction with a default method, but it doesn’t make sense to provide both an autoComplete and a choices method.
"Globally" defined drop-downs
Very often the set of available choices depends on the data type of the property/action parameter, rather than the individual property/parameter itself. And similarly the algorithm to search for references again may well depend only on that reference type.
In the case of choices, annotating a class as "bounded" (as in a "bounded" or fixed number of instances) means that a choices drop-down will automatically be defined. For example:
@DomainObject(
bounded = true
)
public class Product { /* ... */ }
For more on this, see @DomainObject#bounding.
Or, if the data type is an enum, then a drop-down will be provided automatically. A payment method is a good example of this:
public enum PaymentMethod {
VISA, MASTERCARD, AMEX;
}
Something similar can be achieved for autoComplete. Here the domain object indicates a repository query to execute. For example:
@DomainObject(
autoCompleteRepository = Customers.class,
autoCompleteMethod = "findByReferenceOrName"
)
public class Customer { /* ... */ }
with:
@DomainService(nature = NatureOfService.VIEW)
public class Customers {
@Action(semantics=SemanticsOf.SAFE)
public List<Customer> findByReferenceOrName(@MinLength(3) String refOrName) {
...
}
}
For more on this, see @DomainObject#autoCompleteRepository.
There’s no need for the nominated method to be an actual action; any method of any domain service will do, so long as it accepts a string and returns the correct list. |
Multi-select action parameters
As well as scalar values, action parameters can also be collections. For this to be valid, a choices or autoComplete supporting method must be provided.
For example, suppose we want to "tag" or "label" an object:
public StoryCard tag(List<Tag> tags) {
getTags().addAll(tags);
}
public List<Tag> autoCompleteTag(@MinLength(1) search) {
return tagRepository.findByName(search);
}
If the action has been associated with a collection, using @Action#choicesFrom(), then the collection can be used to provide a list of candidate values.
The Web UI (Wicket viewer) handles this by rendering checkboxes against the associated collection; the user can select/deselect these checkboxes and the selected items are taken as the values for the multi-select action.
Dependent choices for action parameters
For action it is also possible (in a limited form) to define dependencies between parameters. Specifically, if one parameter is a drop-down choice, then other drop-down choices can be derived from it.
A good example is a category/sub-category:
public ToDoItem categorize(
Category category,
Subcategory subcategory) {
setCategory(category);
setSubcategory(subcategory);
}
public List<Category> choices0Categorize() {
return categoryRepository.allCategories();
}
public List<Subcategory> choices1Categorize( (1)
Category category) {
return subcategoryRepository.findBy(category);
}
1 | Returns a list of choices for the 2nd parameter based on the argument provided for the first. |
p