There are a few entities in the system we are converting from an ancient language that have attributes that need to be unchangeable once they are set. What is the best way to achieve this behavior?
Hi,
Just o double check: Are you talking about the fact that a particular entity attribute should only be editable in the case of the creation of the entity?
Cheers
Mario
Exactly! I have several attributes that need to be readonly once the entity is created.
Hi,
I prepared an example for you here: GitHub - mariodavid/cuba-example-deactivated-entity-attributes: This CUBA example shows how to deactivate certain attributes of an Entity after entities creation
I will copy over the description of the README for having the information here in the forum as well.
Cheers
Mario
CUBA Example Deactivated Entity Attributes
This CUBA example shows how to deactivate entity attributes once an entity was created.
NOTE: this example only works on the UI layer. In case this constraint has to be fulfilled throughout all parts of the application (like REST API, Entity Inspector etc.) a Bean Validation / EntityChangedEvent based approach has to be used.
Running Example
Variant 1: Manually
The first option is to manually activate a particular entity attribute in case the entity
is new.
This can be achieved by making the field in the XML Screen descriptor non-editable:
<textField
property="identificationNumber"
id="identificationNumberField"
editable="false" />
In the screen controller the entity attribute will be activated in case it is a new entity instance:
@UiController("petclinic_Pet.edit")
@UiDescriptor("pet-edit.xml")
@EditedEntityContainer("petDc")
@LoadDataBeforeShow
public class PetEdit extends StandardEditor<Pet> {
@Inject
protected TextField<String> identificationNumberField;
@Subscribe
protected void onInitEntity(InitEntityEvent<Pet> event) {
identificationNumberField.setEditable(true);
}
}
This variant works in case the amount of fields is not that big. In case there are multiple
entity attributes or the same logic has to be mirrored across different entities the Variant 2 is more suitable.
Variant 2: Screen Mixin
The second option is based on the Screen Mixin functionality which allows to extract particular common login into an Interface.
In this case, the following Interface can be created in the web module WithDeactivatableAttributes
:
/**
* Screen Mixin that allows to define a list of entity attributes, that should be
* deactivated once an entity was created and is in edit mode
*/
public interface WithDeactivatableAttributes {
/**
* the list of Entity attributes that will be deactivated
*/
List<Component.Editable> attributesToDeactivate();
@Subscribe
default void onInit(Screen.BeforeShowEvent event) {
EntityStates entityStates = Extensions.getBeanLocator(event.getSource()).get(EntityStates.class);
Entity editedEntity = ((StandardEditor) event.getSource()).getEditedEntity();
if (!entityStates.isNew(editedEntity)) {
attributesToDeactivate().forEach(
editable -> editable.setEditable(false)
);
}
}
}
The interface has a method that needs to be implemented: attributesToDeactivate
which should return the corresponding list of Component instances that should be deactivated.
A usage of that interface can be found in the VisitEdit
screen:
public class VisitEdit extends StandardEditor<Visit> implements WithDeactivatableAttributes {
@Inject
protected LookupPickerField<Pet> petField;
@Inject
protected DateField<Date> visitDateField;
@Override
public List<Component.Editable> attributesToDeactivate() {
return Arrays.asList(
petField, visitDateField
);
}
}
Excellent - thank you Mario!
Probably one of those approaches will work for our application - however, in the interest of providing examples here on the forums, is there an example somewhere of the other approach you mentioned (using Bean Validation/EntityChangedEvent)?
hi,
probably there is one example out there, but it might be based on the previous common EntityListener interfaces. With CUBA 7.x the EntityChangedEvent has become more relevant.
The idea is:
Create an EntityChangedEventListener and check the via the AttributeChanges changes = event.getChanges();
object for specific changes in for an attribute. Then you can throw an exception in case there was a change.
Cheers
Mario
Hmmmm - that’s probably a little above my head for now. I’m just getting started, but as I said the previous approaches will probably work fine, at least for now.