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):
a JS snippet
executeInteractiveLegendScript
(with a line to create a Primefaces widget) andan 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.