You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/09/21 08:05:50 UTC

[isis] branch master updated: ISIS-3215: first attempt on a fix

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 868c8ff529 ISIS-3215: first attempt on a fix
868c8ff529 is described below

commit 868c8ff5295f421c80f436eb1185c74a10be8926
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Sep 21 10:05:44 2022 +0200

    ISIS-3215: first attempt on a fix
---
 .../components/actions/ActionParametersForm.java   |  5 +-
 .../ui/components/property/PropertyEditForm.java   |  2 +-
 ...riber.java => ScalarModelChangeDispatcher.java} | 20 ++++++-
 ...scriber.java => ScalarModelChangeListener.java} |  2 +-
 .../ui/components/scalars/ScalarPanelAbstract.java | 66 +++++++++++++++-------
 .../scalars/ScalarPanelSelectAbstract.java         |  8 ++-
 .../scalars/reference/ReferencePanel.java          | 34 ++++++++++-
 .../valuechoices/ValueChoicesSelect2Panel.java     |  2 +-
 .../ui/components/widgets/select2/Select2.java     |  9 ++-
 .../widgets/select2/Select2OnSelect.java           | 52 ++++++++++++-----
 .../wicket/ui/panels/PromptFormAbstract.java       |  4 +-
 11 files changed, 152 insertions(+), 52 deletions(-)

diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersForm.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersForm.java
index e75fb3625d..a4a4a07a1c 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersForm.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/actions/ActionParametersForm.java
@@ -45,9 +45,10 @@ import org.apache.isis.viewer.wicket.ui.panels.PromptFormAbstract;
 import org.apache.isis.viewer.wicket.ui.util.Wkt;
 import org.apache.isis.viewer.wicket.ui.util.WktDecorators;
 
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
 import lombok.val;
 
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
+
 class ActionParametersForm
 extends PromptFormAbstract<ActionModel> {
 
@@ -100,7 +101,7 @@ extends PromptFormAbstract<ActionModel> {
 
         _Casts.castTo(ScalarPanelAbstract.class, component)
         .ifPresent(scalarPanel->{
-            scalarPanel.notifyOnChange(this); // handling onUpdate and onError
+            scalarPanel.addChangeListener(this); // handling onUpdate and onError
             onNewScalarPanel.accept(scalarPanel);
         });
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/property/PropertyEditForm.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/property/PropertyEditForm.java
index 9ecf90fb52..1085ceaeea 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/property/PropertyEditForm.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/property/PropertyEditForm.java
@@ -58,7 +58,7 @@ extends PromptFormAbstract<ScalarPropertyModel> {
 
         _Casts.castTo(ScalarPanelAbstract.class, component)
         .ifPresent(scalarModelSubscriber->
-            scalarModelSubscriber.notifyOnChange(this)); // handling onUpdate and onError
+            scalarModelSubscriber.addChangeListener(this)); // handling onUpdate and onError
     }
 
     @Override
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelSubscriber.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelChangeDispatcher.java
similarity index 61%
copy from viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelSubscriber.java
copy to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelChangeDispatcher.java
index 53355f20fb..e3d2b39dac 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelSubscriber.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelChangeDispatcher.java
@@ -20,10 +20,24 @@ package org.apache.isis.viewer.wicket.ui.components.scalars;
 
 import org.apache.wicket.ajax.AjaxRequestTarget;
 
-public interface ScalarModelSubscriber {
+import lombok.NonNull;
+import lombok.val;
 
-    void onUpdate(AjaxRequestTarget target, ScalarPanelAbstract scalarPanel);
+public interface ScalarModelChangeDispatcher {
 
-    void onError(AjaxRequestTarget target, ScalarPanelAbstract scalarPanel);
+    @NonNull Iterable<ScalarModelChangeListener> getOnChangeListeners();
+    @NonNull ScalarPanelAbstract getScalarPanel();
+
+    default void notifyUpdate(final AjaxRequestTarget target) {
+        for (val listener : getOnChangeListeners()) {
+            listener.onUpdate(target, getScalarPanel());
+        }
+    }
+
+    default void notifyError(final AjaxRequestTarget target) {
+        for (val listener : getOnChangeListeners()) {
+            listener.onError(target, getScalarPanel());
+        }
+    }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelSubscriber.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelChangeListener.java
similarity index 96%
rename from viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelSubscriber.java
rename to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelChangeListener.java
index 53355f20fb..dce81bd1b2 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelSubscriber.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarModelChangeListener.java
@@ -20,7 +20,7 @@ package org.apache.isis.viewer.wicket.ui.components.scalars;
 
 import org.apache.wicket.ajax.AjaxRequestTarget;
 
-public interface ScalarModelSubscriber {
+public interface ScalarModelChangeListener {
 
     void onUpdate(AjaxRequestTarget target, ScalarPanelAbstract scalarPanel);
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
index 720254b970..566f5c8293 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
@@ -22,6 +22,8 @@ import java.util.EnumSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
@@ -31,6 +33,7 @@ import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.feedback.ComponentFeedbackMessageFilter;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.model.IModel;
 import org.springframework.lang.Nullable;
 
@@ -41,6 +44,7 @@ import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.commons.internal.debug._Probe;
 import org.apache.isis.commons.internal.debug._Probe.EntryPoint;
+import org.apache.isis.commons.internal.debug._XrayEvent;
 import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
 import org.apache.isis.core.metamodel.facets.objectvalue.labelat.LabelAtFacet;
 import org.apache.isis.core.metamodel.object.ManagedObject;
@@ -51,6 +55,7 @@ import org.apache.isis.viewer.commons.model.decorators.FormLabelDecorator.FormLa
 import org.apache.isis.viewer.commons.model.scalar.UiParameter;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.model.util.PageParameterUtils;
 import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.AdditionalLinksPanel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.FrameFragment;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.RegularFrame;
@@ -74,7 +79,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel
 
 public abstract class ScalarPanelAbstract
 extends PanelAbstract<ManagedObject, ScalarModel>
-implements ScalarModelSubscriber {
+implements ScalarModelChangeListener {
 
     private static final long serialVersionUID = 1L;
 
@@ -303,8 +308,8 @@ implements ScalarModelSubscriber {
 
         addCssFromMetaModel();
 
-        notifyOnChange(this);
-        addFormComponentBehaviourToUpdateSubscribers();
+        addChangeListener(this);
+        installScalarModelUpdateDispatcher();
     }
 
     protected abstract void setupInlinePrompt();
@@ -447,17 +452,41 @@ implements ScalarModelSubscriber {
 
     // //////////////////////////////////////
 
+    protected void installScalarModelUpdateDispatcher() {
+        addOrReplaceBehavoir(ScalarUpdatingBehavior.class, ()->new ScalarUpdatingBehavior(this));
+    }
 
-    static class ScalarUpdatingBehavior extends AjaxFormComponentUpdatingBehavior {
+    static class ScalarUpdatingBehavior extends AjaxFormComponentUpdatingBehavior
+    implements ScalarModelChangeDispatcher {
         private static final long serialVersionUID = 1L;
 
+        @Getter(onMethod_={@Override})
         private final ScalarPanelAbstract scalarPanel;
 
+        @Override
+        public @NonNull Iterable<ScalarModelChangeListener> getOnChangeListeners() {
+            return scalarPanel.onChangeListeners;
+        }
+
         private ScalarUpdatingBehavior(final ScalarPanelAbstract scalarPanel) {
             super("change");
             this.scalarPanel = scalarPanel;
         }
 
+        @Override
+        protected Form.MethodMismatchResponse onMethodMismatch() {
+            onBeforeRequest(); // onRequest method in super is final, hooking into onMethodMismatch is trick
+            return super.onMethodMismatch();
+        }
+
+        private void onBeforeRequest() {
+            val requestArgs = PageParameterUtils.streamCurrentRequestParameters()
+                    .map(pair->String.format("%s->%s", pair.getKey(), pair.getValue()))
+                    .collect(Collectors.joining(", "));
+
+            _XrayEvent.event("onRequest %s%n", requestArgs);
+        }
+
         @Override
         protected void onUpdate(final AjaxRequestTarget target) {
 
@@ -467,35 +496,32 @@ implements ScalarModelSubscriber {
 
             _Xray.onUserParamOrPropertyEdit(scalarPanel);
 
-            for (ScalarModelSubscriber subscriber : scalarPanel.subscribers) {
-                subscriber.onUpdate(target, scalarPanel);
-            }
+            notifyUpdate(target);
         }
 
         @Override
         protected void onError(final AjaxRequestTarget target, final RuntimeException e) {
             super.onError(target, e);
-            for (ScalarModelSubscriber subscriber : scalarPanel.subscribers) {
-                subscriber.onError(target, scalarPanel);
-            }
+            notifyError(target);
         }
+
     }
 
-    private final List<ScalarModelSubscriber> subscribers = _Lists.newArrayList();
+    @Getter
+    private final List<ScalarModelChangeListener> onChangeListeners = _Lists.newArrayList();
 
-    public void notifyOnChange(final ScalarModelSubscriber subscriber) {
-        subscribers.add(subscriber);
+    public void addChangeListener(final ScalarModelChangeListener subscriber) {
+        onChangeListeners.add(subscriber);
     }
 
-    private void addFormComponentBehaviourToUpdateSubscribers() {
+    protected final <T extends Behavior> void addOrReplaceBehavoir(
+            final @NonNull Class<T> behaviorClass, final @NonNull Supplier<T> factory) {
         val validationFeedbackReceiver = getValidationFeedbackReceiver();
-        if(validationFeedbackReceiver == null) {
-            return;
-        }
-        for (Behavior b : validationFeedbackReceiver.getBehaviors(ScalarUpdatingBehavior.class)) {
-            validationFeedbackReceiver.remove(b);
+        if(validationFeedbackReceiver == null) { return; }
+        for (val behavior : validationFeedbackReceiver.getBehaviors(behaviorClass)) {
+            validationFeedbackReceiver.remove(behavior);
         }
-        validationFeedbackReceiver.add(new ScalarUpdatingBehavior(this));
+        validationFeedbackReceiver.add(factory.get());
     }
 
     // //////////////////////////////////////
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java
index f4d721da62..e25bfc2f95 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java
@@ -52,7 +52,7 @@ extends ScalarPanelFormFieldAbstract<ManagedObject> {
 
     protected Select2 select2;
 
-    public ScalarPanelSelectAbstract(
+    protected ScalarPanelSelectAbstract(
             final String id,
             final ScalarModel scalarModel) {
         super(id, scalarModel, ManagedObject.class);
@@ -61,10 +61,12 @@ extends ScalarPanelFormFieldAbstract<ManagedObject> {
 
     protected final Select2 createSelect2(
             final String id,
-            final Function<ScalarModel, ChoiceProviderAbstract> choiceProviderFactory) {
+            final Function<ScalarModel, ChoiceProviderAbstract> choiceProviderFactory,
+            final ScalarModelChangeDispatcher select2ChangeDispatcher) {
         val scalarModel = scalarModel();
         val select2 = Select2.createSelect2(id, scalarModel,
-                choiceProviderFactory.apply(scalarModel));
+                choiceProviderFactory.apply(scalarModel),
+                select2ChangeDispatcher);
         val settings = select2.getSettings();
         switch(scalarModel.getChoiceProviderSort()) {
         case CHOICES:
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
index b484b5611f..5ee2fbc9e5 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.scalars.reference;
 
+import java.io.Serializable;
 import java.util.Optional;
 
 import org.apache.wicket.Application;
@@ -36,6 +37,8 @@ import org.apache.isis.viewer.commons.model.components.UiComponentType;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.CompactFragment;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarFragmentFactory.FieldFrame;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelChangeDispatcher;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelChangeListener;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelSelectAbstract;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelSelectAbstract.ChoiceTitleHandler;
 import org.apache.isis.viewer.wicket.ui.components.widgets.entitysimplelink.EntityLinkSimplePanel;
@@ -43,6 +46,8 @@ import org.apache.isis.viewer.wicket.ui.components.widgets.select2.providers.Cho
 import org.apache.isis.viewer.wicket.ui.util.Wkt;
 import org.apache.isis.viewer.wicket.ui.util.WktComponents;
 
+import lombok.Getter;
+import lombok.NonNull;
 import lombok.val;
 
 /**
@@ -88,12 +93,37 @@ implements ChoiceTitleHandler {
         this.entityLink = new EntityLinkSelect2Panel(UiComponentType.ENTITY_LINK.getId(), this);
         entityLink.setRequired(scalarModel.isRequired());
 
-        this.select2 = createSelect2(ID_AUTO_COMPLETE, ChoiceProviderForReferences::new);
+        this.select2 = createSelect2(ID_AUTO_COMPLETE,
+                ChoiceProviderForReferences::new, new Select2ChangeDispatcher(this));
 
         entityLink.addOrReplace(select2.asComponent());
         entityLink.setOutputMarkupId(true);
 
-        return this.entityLink;
+        return entityLink;
+    }
+
+    // -- CUSTOM UPDATING BEHAVIOR
+
+    @Override
+    protected void installScalarModelUpdateDispatcher() {
+        // no-op
+    }
+
+    static class Select2ChangeDispatcher
+    implements ScalarModelChangeDispatcher, Serializable {
+        private static final long serialVersionUID = 1L;
+
+        @Getter(onMethod_={@Override})
+        private final ReferencePanel scalarPanel;
+
+        private Select2ChangeDispatcher(final ReferencePanel scalarPanel) {
+            this.scalarPanel = scalarPanel;
+        }
+
+        @Override
+        public @NonNull Iterable<ScalarModelChangeListener> getOnChangeListeners() {
+            return scalarPanel.getOnChangeListeners();
+        }
     }
 
     // -- ON BEFORE RENDER
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
index 9a83fbe88e..80837d320f 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/valuechoices/ValueChoicesSelect2Panel.java
@@ -57,7 +57,7 @@ implements ChoiceTitleHandler {
     protected FormComponent<ManagedObject> createFormComponent(
             final String id, final ScalarModel scalarModel) {
         if(select2 == null) {
-            this.select2 = createSelect2(id, ChoiceProviderForValues::new);
+            this.select2 = createSelect2(id, ChoiceProviderForValues::new, null);
         } else {
             select2.clearInput();
         }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java
index c864260ecb..9fb2d15fda 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2.java
@@ -39,6 +39,7 @@ import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.model.models.ScalarModelWithMultiChoice;
 import org.apache.isis.viewer.wicket.model.models.ScalarModelWithSingleChoice;
 import org.apache.isis.viewer.wicket.model.util.WktContext;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelChangeDispatcher;
 import org.apache.isis.viewer.wicket.ui.components.widgets.select2.providers.ChoiceProviderAbstract;
 import org.apache.isis.viewer.wicket.ui.components.widgets.select2.providers.ChoiceProviderAbstractForScalarModel;
 
@@ -61,7 +62,8 @@ implements
     public static Select2 createSelect2(
             final String id,
             final ScalarModel scalarModel,
-            final ChoiceProviderAbstract choiceProvider) {
+            final ChoiceProviderAbstract choiceProvider,
+            final ScalarModelChangeDispatcher select2ChangeDispatcher) {
         val select2 = new Select2(scalarModel.isSingular()
                 ? Either.left(
                         Select2ChoiceExt.create(id,
@@ -78,7 +80,9 @@ implements
         select2.getSettings().setWidth("100%");
 
         // listen on select2:select/unselect events (client-side)
-        select2.add(new Select2OnSelect(scalarModel));
+        if(select2ChangeDispatcher!=null) {
+            select2.add(new Select2OnSelect(scalarModel, select2ChangeDispatcher));
+        }
 
         return select2;
     }
@@ -183,7 +187,6 @@ implements
         });
     }
 
-    
 
     // -- HELPER
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java
index 59f103ea62..133b26f5ee 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/select2/Select2OnSelect.java
@@ -19,24 +19,32 @@
 package org.apache.isis.viewer.wicket.ui.components.widgets.select2;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 
 import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.behavior.AbstractAjaxBehavior;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.INamedParameters.NamedPair;
+import org.springframework.lang.Nullable;
 import org.wicketstuff.select2.JQuery;
 
 import org.apache.isis.commons.binding.Bindable;
 import org.apache.isis.commons.collections.Can;
+import org.apache.isis.commons.internal.debug._XrayEvent;
+import org.apache.isis.commons.internal.debug.xray.XrayUi;
 import org.apache.isis.core.metamodel.object.ManagedObject;
 import org.apache.isis.core.metamodel.object.PackedManagedObject;
 import org.apache.isis.core.metamodel.objectmanager.memento.ObjectMemento;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.model.util.PageParameterUtils;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelChangeDispatcher;
 
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -52,8 +60,10 @@ import lombok.val;
  */
 @RequiredArgsConstructor
 class Select2OnSelect extends AbstractAjaxBehavior {
+
     private static final long serialVersionUID = 1L;
     private final ScalarModel scalarModel;
+    private final ScalarModelChangeDispatcher select2ChangeDispatcher;
 
     private static enum Event {
         SELECT, UNSELECT, CLEAR;
@@ -71,6 +81,7 @@ class Select2OnSelect extends AbstractAjaxBehavior {
     @Override
     public void renderHead(final Component component, final IHeaderResponse response) {
         for(var event : Event.values()) {
+            //response.render(OnDomReadyHeaderItem.forScript("Wicket.Log.enabled=true;"));
             response.render(OnDomReadyHeaderItem.forScript(JQuery.execute("$('#%s')"
                     + ".on('select2:%s', function (e) {"
                     + "var data = e.params.data;"
@@ -85,11 +96,15 @@ class Select2OnSelect extends AbstractAjaxBehavior {
         }
     }
 
+    @Override
+    public void onRequest() {
+        updatePendingModels();
+    }
+
     /**
      * update the param negotiation model
      */
-    @Override
-    public void onRequest() {
+    private void updatePendingModels() {
         PageParameterUtils.streamCurrentRequestParameters()
         .forEach(pair->
             Event.valueOf(pair).ifPresent(event->{
@@ -98,20 +113,14 @@ class Select2OnSelect extends AbstractAjaxBehavior {
                     val component = (Select2MultiChoiceExt)getComponent();
                     switch(event) {
                     case SELECT:{
-                        val selection = component.getModelObject();
-                        val newSelection = selection!=null
-                                ? new ArrayList<ObjectMemento>(selection)
-                                : new ArrayList<ObjectMemento>();
+                        val newSelection = copySelection(component.getModelObject());
                         newSelection.add(objectMementoFromEvent);
                         component.setModelObject(newSelection);
                         updateReceiver().setValue(demementify(newSelection));
                         break;
                     }
                     case UNSELECT:{
-                        val selection = component.getModelObject();
-                        val newSelection = selection!=null
-                                ? new ArrayList<ObjectMemento>(selection)
-                                : new ArrayList<ObjectMemento>();
+                        val newSelection = copySelection(component.getModelObject());
                         newSelection.remove(objectMementoFromEvent);
                         component.setModelObject(newSelection);
                         updateReceiver().setValue(demementify(newSelection));
@@ -122,8 +131,6 @@ class Select2OnSelect extends AbstractAjaxBehavior {
                         clearUpdateReceiver();
                         break;
                     }
-//                    System.err.printf("value updated %s%n",
-//                            scalarModel.getObjectManager().demementify(objectMementoFromEvent));
                 }
                 else
                 if(getComponent() instanceof Select2ChoiceExt) {
@@ -142,7 +149,19 @@ class Select2OnSelect extends AbstractAjaxBehavior {
 
                 } else return;
 
-                //TODO schedule form update (AJAX)
+                if(XrayUi.isXrayEnabled()) {
+                    _XrayEvent.event("Select2 event: %s %s", event, objectMementoFromEvent.getBookmark());
+                }
+
+                // schedule form update (AJAX)
+
+                WebApplication app = (WebApplication)getComponent().getApplication();
+                AjaxRequestTarget target = app.newAjaxRequestTarget(getComponent().getPage());
+
+                select2ChangeDispatcher.notifyUpdate(target);
+
+                RequestCycle requestCycle = RequestCycle.get();
+                requestCycle.scheduleRequestHandlerAfterCurrent(target);
 
             })
         );
@@ -150,6 +169,12 @@ class Select2OnSelect extends AbstractAjaxBehavior {
 
     // -- HELPER
 
+    private List<ObjectMemento> copySelection(final @Nullable Collection<ObjectMemento> outdatedSelection) {
+        return outdatedSelection!=null
+                ? new ArrayList<>(outdatedSelection)
+                : new ArrayList<>(1);
+    }
+
     private ManagedObject demementify(final ObjectMemento memento) {
         return scalarModel.getObjectManager().demementify(memento);
     }
@@ -187,5 +212,4 @@ class Select2OnSelect extends AbstractAjaxBehavior {
                 prop.getPendingPropertyModel().clear());
     }
 
-
 }
\ No newline at end of file
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java
index e21da0a7d0..3e78cf0da3 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/PromptFormAbstract.java
@@ -42,7 +42,7 @@ import org.apache.isis.viewer.wicket.model.models.ActionModel;
 import org.apache.isis.viewer.wicket.model.models.ActionPromptProvider;
 import org.apache.isis.viewer.wicket.model.models.FormExecutorContext;
 import org.apache.isis.viewer.wicket.model.models.ScalarPropertyModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelSubscriber;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarModelChangeListener;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
 import org.apache.isis.viewer.wicket.ui.pages.entity.EntityPage;
@@ -56,7 +56,7 @@ public abstract class PromptFormAbstract<T extends
     FormExecutorContext
     & IModel<ManagedObject>>
 extends OkCancelForm<T>
-implements ScalarModelSubscriber {
+implements ScalarModelChangeListener {
 
     private static final long serialVersionUID = 1L;