Databinding with a list of child Objects

data class OTitleDetail (
    val titleElements : MutableList<OTitleElement> = mutableListOf() 
)

Is it possible with a BeanValidationBinder<OTitleDetail> to bind properties of the OTitleElement objects?

Or to ask it in another way: If we have to create a single BeanValidationBinder<OTitleElement> for each of the child elements, what would be the proper way to chain validation of all the binders we would get as a result.

Hi Thomas,

Not sure if you’re asking for a generic way of handling a group of binders or something else. Anyway, I needed to handle a group of binders because I have multiple entities and they are not always of the same type. Here’s what I did:

/**
 * The BinderGroup is used to register and manage multiple binders.
 * 
 * @author martinisraelsen
 *
 */
@Tag("BinderGroup")
@SpringComponent
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BinderGroup extends Component implements HasLogger {

   @SuppressWarnings("rawtypes")
   Map<Binder, Object> bindings = new HashMap<Binder, Object>();

   List<FormDataChangedListener> changedListeners = new ArrayList<FormDataChangedListener>();

   List<RecalculateEventListener> recalculateListeners = new ArrayList<RecalculateEventListener>();
   /**
    * Manual change override for fields that are not bound by the binder
    */
   private boolean changed;

   /**
    * Is true if all binders are valid
    */
   private boolean valid;

   /**
    * If set to true, onChange will not be marking a change
    */
   private boolean inhibit;

   public void addRecalcListener(RecalculateEventListener listener) {
      recalculateListeners.add(listener);
   }

   public void addFormDataChangedListener(FormDataChangedListener listener) {
      changedListeners.add(listener);
   }

   /**
    * Registers a binder
    * 
    * @param binder
    * @param o
    */
   public void register(Binder<?> binder, Object o) {
      boolean addListener = !bindings.containsKey(binder);
      bindings.put(binder, o);

      if (addListener) {
         binder.addValueChangeListener(e -> {

            if (!e.isFromClient())
               return;

            boolean newChanged = (!Util.equals(e.getOldValue(), e.getValue()));
            boolean newIsValid = this.isValid();

            if (changed != newChanged || valid != newIsValid) {
               this.valid = newIsValid;
               setChanged(newChanged);
            }
         });
      }
   }

   /**
    * Unregisters a binder.
    * 
    * @param b
    */
   public void unregister(Binder<?> b) {
      bindings.remove(b);
   }

   public void clear() {
      bindings.clear();
   }

   /**
    * Returns true if all registered beans are valid
    * 
    * @return
    */
   public boolean isValid() {
      return bindings.keySet().stream().allMatch(e -> e.isValid());
   }

   /**
    * Returns true if any of the registered beans have changes.
    * 
    * @return
    */
   public boolean hasChanges() {
      return changed || bindings.keySet().stream().anyMatch(e -> e.hasChanges());
   }

   /**
    * Indicates a change not handled by binders
    * 
    * @param b
    */
   public void setChanged(boolean b) {

      if (inhibit)
         return;

      this.changed = b;
      this.valid = isValid();

      // Manually fire event
      for (FormDataChangedListener f : changedListeners) {
         f.dataChanged(new FormDataChangedEvent(this, this.changed, this.valid));
      }
   }

   /**
    * Turns off "listining" for changes. Useful when reloading data.
    * 
    * @param b
    */
   public void setInhibit(boolean b) {
      inhibit = b;
   }

   /**
    * Writes all registered beans. Throws a validation exception if unable to
    * write.
    * 
    * @throws ValidationException
    */
   @SuppressWarnings({ "unchecked", "rawtypes" })
   public void writeBeans() throws ValidationException {
      for (Binder b : bindings.keySet()) {
         if (bindings.get(b) != null) {
            b.writeBean(bindings.get(b));
         }
      }
   }

   /**
    * Fires a recalc request, unless this has been disabled with the inhibit
    * flag
    */
   public void fireRecalcRequest() {
      if (inhibit)
         return;

      for (RecalculateEventListener e : recalculateListeners) {
         e.recalculate(null);
      }
   }
}