1

It's a follow-up to this question.

Environment

Primefaces 8.0.RC3
JSF 2.2.15
Tomcat 8.5.47

Background

I have a custom JSF component that extends UIComponentBase.

public final class Map extends UIComponentBase implements Widget {
    // resolveWidgetVar
    // a constructor
    // getters/setters
}

There is MapRenderer which will be rendering that component.

public final class MapRenderer extends CoreRenderer {

    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
        final Map map = (Map) component;

        encodeMarkup(context, map);
        encodeScript(context, map);
    }

    private void encodeMarkup(FacesContext context, Map map) throws IOException {
        // renders a div
    }

    private void encodeScript(FacesContext context, Map map) throws IOException {
        // renders a script with PF.cw(widgetName, widgetVar, cfg)
    }

}

It's going to be a leaflet-based map with a legend defined as a SelectManyMenu component. The legend serves as both a legend and a filter. The challenge is to nicely pass everything needed to the JS side. For that, I employ the approach shown by this answer - I basically encode a programmatically created SelectManyMenu to my Writer.

Then, I split it into 2 parts (because of the way controls (here, legends) should be defined - see the function below):

  1. a JS snippet executeInteractiveLegendScript (with a line to create a Primefaces widget) and

  2. an HTML snippet getInteractiveLegendHTML (a markup of the menu).


function (configuration) {
    var interactiveLegend = L.control({position: 'bottomleft'});

    interactiveLegend.onAdd = function () {
        var container = L.DomUtil.create('div', 'interactive-legend');
        container.innerHTML = configuration['getInteractiveLegendHTML']();
        return container;
    };

    interactiveLegend.addTo(map);
    configuration['executeInteractiveLegendScript']();
}

configuration is an object I have compiled on the Java side. At some point, by means of JS, I add the legend to the map. The JS snippet is being executed correctly, the Primefaces element is being displayed correctly - no problems here.

Problem

The problem is in defining client behaviours for this programmatically created component.

// while rendering the map

SelectManyMenu selectManyMenu = (SelectManyMenu) context.getApplication()
    .createComponent(SelectManyMenu.COMPONENT_TYPE);

// setting children and properies
selectManyMenu.addClientBehavior("change", createAjax(context));

selectManyMenu.encodeAll(context);

The method to create a ClientBehavior is defined as follows

private ClientBehavior createAjax(FacesContext context) {
    Application application = context.getApplication();
    ExpressionFactory expressionFactory = application.getExpressionFactory();
    AjaxBehavior behavior = (AjaxBehavior) application.createBehavior(AjaxBehavior.BEHAVIOR_ID);

    MethodExpression methodExpression = expressionFactory.createMethodExpression(context.getELContext(), "#{myexpression}", Void.TYPE, new Class[]{String.class});
    AjaxBehaviorListenerImpl listener = new AjaxBehaviorListenerImpl(methodExpression, methodExpression, methodExpression);

    behavior.setProcess("@this");

    // PROBLEM HERE - this listener never gets invoked
    behavior.addAjaxBehaviorListener(listener);

    return behavior;
}

I doubt the place where I register this client behaviour, could it be that it's too late and the listener simply doesn't get registered properly?

The question is how to make it work, and where should I create a menu instance and bind all the properties/listeners.

@BalusC, in his answer, mentioned

(assuming that you're sitting in invoke application phase; when sitting in render response phase, this needs to be done differently):

which confirms I am doing something wrong.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
  • I fail to see the actual usecase for which you think this approach is the problem? It feels 'hacky'. Off-topic: begging (asking) for help in a comment is not appreciated... – Kukeltje Feb 19 '20 at 15:17
  • @Kukeltje It's hacky and dirty. I just couldn't come up with a better approach to implement this. – Andrew Tobilko Feb 19 '20 at 15:19
  • But if your actual usecase is not clear, we cannot help to see what the better approach would be. – Kukeltje Feb 19 '20 at 15:21
  • @Kukeltje could you elaborate on what exactly is unclear? I tried to be as specific as possible. The ajax listener never gets invoked because it was probably registered at the wrong place – Andrew Tobilko Feb 19 '20 at 15:22
  • Using a selectOneMenu as a legend AND a filter... As a filter is clear, as a legend totally not you only see one thing until you open it. A selectOneRadio would be 'clearer in that regard. So everything before your technical issue of the 'splitting into two', adding to the map etc... What do you want to achieve on a ui-design level. And what part of the technical problem is 'leaflet' related? Or is the tag applied just because you use it? – Kukeltje Feb 19 '20 at 15:29
  • @Kukeltje Sorry, I mentioned `SelectManyMenu` (this one - https://www.primefaces.org/showcase/ui/input/manyMenu.xhtml) – Andrew Tobilko Feb 19 '20 at 15:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/208135/discussion-between-kukeltje-and-andrew-tobilko). – Kukeltje Feb 19 '20 at 15:37

0 Answers0