I am trying to implement an audit trail functionality for my web application that records:
for each of my input fields (input fields are spread over several forms with different backing beans and different valueHolder classes).
The first two (lastModified and modifiedBy) are easily done with the help of an JPA AuditListener and @PrePersit and @PreUpdate methods.
The third one is a bit tricky since it requires user interaction. Best would be a dialog that asks for the user comment.
So there are (at least) two open issues: Can I establish a "global" valueChangeListener for all input fields in my application? Is this possible without attaching <f:valueChangeListener>
to each single input component? Second: How can I grab the user comment. My idea is to put a p:dialog
in my web page template but this dialog needs to know from which input component it is called.
Can I establish a "global" valueChangeListener for all input fields in my application? Is this possible without attaching to each single input component?
Yes, with a SystemEventListener
which get executed during PreRenderViewEvent
. You need to walk through the component tree as obtained by FacesContext#getViewRoot()
to find all components which are an instanceof
EditableValueHolder
(or something more finer-grained) and then add the new YourValueChangeListener()
by the addValueChangeListener()
method. See also this answer how to register the system event listener: How to apply a JSF2 phaselistener after viewroot gets built?
Second: How can I grab the user comment. My idea is to put a p:dialog in my web page template but this dialog needs to know from which input component it is called.
You could in YourValueChangeListener#processValueChange()
set the component in question as a property of some request or view scoped which you grab by evaluateExpressionGet()
.
Recorder recorder = (Recorder) context.getApplication().evaluateExpressionGet(context, "#{recorder}", Recorder.class);
recorder.setComponent(event.getComponent());
// ...
It will execute the EL and auto-create the bean in its scope if necessary. The bean in turn should also hold the property representing the user comment. Finally, you can use it in your <p:dialog>
.
<p>You have edited #{recorder.component.label}, please mention the reason:</p>
...
<h:inputText value="#{recorder.comment}" />