Subscribing to PostValidationEvent of dynamicaly created child component - jsf-2

For a forms framework in which I like to use JSF as the real UI frontend, I am searching for a way that a parent component gets informed if in a child component the value is changed. The facelet of a basic 'control' looks like this(body/head omitted since no-one can run it anyway without a dozen classes):
<xf:input ref="/my/xpath/value">
<xf:label>Label</xf:label>
</xf:input>
The xf:input component which I developed, dynamically creates a real ui component (PrimeFaces ones) based on the type of the value that ref="/my/xpath/value" points to. This real ui component is created in a preRenderView event like is done in this example. It is handled in the following method in the 'parent' control
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
if (!context.isPostback()) {
control = createControl(context);
//context.getApplication().unsubscribeFromEvent(PostValidateEvent.class, getControl().getClass(), this);
control.subscribeToEvent(PostValidateEvent.class, this);
}
}
The actual controls all have an programmatically added ajax handler added to it, which makes it possible to just process the specific input ('implicit ajax'). Default JSF component validations are normally applied and this all works great.
The issue/challenge is that in this 'wrapper' component I'd like to be informed of value changes after the validation. My first idea was to us the subscribeEvent on the dynamically added control like this:
control.subscribeToEvent(PostValidateEvent.class, this);
The subscribing works, but on the postback, an NPE is thrown in the UIComponent (Mojarra 2.2.9) because the wrapped is null in the following method
public boolean isListenerForSource(Object component) {
if (wrapped instanceof SystemEventListener) {
return ((SystemEventListener) wrapped).isListenerForSource(component);
} else {
return instanceClass.isAssignableFrom(component.getClass());
}
}
This might be because the actual component seems to be newly created when the data is submitted en hence the 'subscription' is lost.
Registering on the ViewRoot does not work since the source of the event is always the ViewRoot and registering on the Application is plain wrong.
It might be that I'm looking for a solution in the wrong direction but for now I'm clueless. Keep in mind that I have no direct control over the created ui controls, nor do I want to override their renderers if I can prevent to. So signalling the parent from the child control is not an option to.
Other things I tried:
Using valueChangeListeners but that did not work either with lots of other problems (including ways to make it extensible)
Using composite components with binding but that failed including them dynamically, requiring naming containers that conflict with the id's required by the rest of the framework, the positions of labels, hints and alerts in the xhtml and/or resulting dom
Taghandlers to manipulate the tree when creating them
This all is with Mojarra up to 2.2.9 (did not check newer yet or MyFaces)

Adding the component in the PreRenderViewEvent works nicely. The thing is that you do not seem to be able to have subscriptions to events survive a request. The actual components are recreated (in the RestoreViewPhase I assume, did not check) and then the subscription to the event is still there, just the 'wrapped' context where it should be called is empty.
Adding the PostValidationEvent event in the PostRestoreStateEvent of this specific component (it is the only one in the FacesContext.getCurrentInstance().getPartialViewContext().getExecuteIds()) makes it fire as mentioned in the comments. The trick (hack/workaround/...) to get rid of the NPE in the next request is to actually remove the event again.
((UIComponent) event.getSource()).unsubscribeFromEvent(PostValidateEvent.class, this);
I'll try to create an example without any PrimeFaces or OmniFaces and see what happens then since they both seem to be wrappers around the context and I want to make sure they are not the cause of the behaviour.

Related

how to access ShadowDOM of other polymer elements?

I'm learning Dart by making a simple webapp. the app ui I have in mind has two parts, one is a control panel, the other is a workspace. by clicking buttons in the control panel, user should be able to control the workspace.
both the control panel and the workspace are custom polymer elements. In the Control Panel's dart class, I can access itself by using shadowRoot.querySelector, but since the control panel needs to control the workspace, I need to access the workspace also. but I don't know how to do that. I tried querySelector for example, It gave me null. I understand it is a shadow DOM in the workspace tag, but how to access other tags' shadow DOM?
I can't find anything online, every example and document seems to only use shadowRoot to access self elements.
It is difficult to access the shadow DOM of another element, and this is by design. Instead of having your two custom elements so tightly coupled, a better approach would be to use events or signals. Your control panel element should take user input and fire appropriate events using the convenient fire() method it inherits from the PolymerElement class. Your application can catch and then relay those events to your workspace element. If that seems overly circuitous, you can use Polymer's <core-signals> element to pass events without dealing with intermediaries.
As an example, inside your control panel element, you might have a bold button.
<button on-click="{{boldClicked}}">Bold</button>
When that button is clicked, the control panel's boldClicked() method is executed in response. It might look something like this:
void boldClicked(Event event, var detail, Element target) {
fire('core-signal', detail: {'name': 'bold', 'data': null});
}
Then in your workspace element's HTML file, you might have:
<core-signals on-core-signal-bold="{{boldEventReceived}}"></core-signals>
And finally, in your workspace element's Dart class would be a method like so:
void boldEventReceived(Event event, var detail, Element sender) {
// manipulate workspace shadow DOM here
}
This is just one of several ways to accomplish this. You can look over the Dart team's <core-signals> example for more.
And of course, if you're using Polymer to its full potential, you will find that you need to do very little manual DOM manipulation. Using data binding and data-driven views is a winning strategy.
You can either use a selector that pierces though all shadow boundaries querySelector('my-tag /deep/ some-element') or querySelector('* /deep/ some-element') or as selector that just pierces through one level of shadow boundary querySelector('my-tag::shadow some-element') or alternatively
place both elements within the <template> of another Polymer element then you can connect attributes of both components with the same field on the common parent element (this is the preferred method in Polymer.
The solution of #user3216897 is fine of course especially if the elements don't share a common parent.
Instead of shadowRoot.querySelector you should be able to use $['abc'] if the element has an id attribute with the value 'abc'.

Rendering Polymer element once per multiple attribute changes

I have an Polymer.dart element with multiple attributes, e.g.
<code-mirror lines="{{lines}}" widgets="{{widgets}}">
</code-mirror>
on some occasions lines and widgets change simultaneously sometimes only widgets changes.
I would like to rerender component once independently on how many properties change in the same turn of event loop.
Is there a way a good built-in way to achieve that?
Additional trouble here is that interpretation of widgets depends on content of lines and ordering in which linesChanged and widgetsChanged callbacks arrive is browser dependent, e.g. on Firefox widgetsChanged arrives first before linesChanged and component enters inconsistent state if I do any state management in the linesChanged callback.
Right now I use an auxiliary class like this:
class Task {
final _callback;
var _task;
Task(this._callback);
schedule() {
if (_task == null) {
_task = new async.Timer(const Duration(milliseconds: 50), () {
_task = null;
_callback();
});
}
}
}
final renderTask = new Task(this._render);
linesChanged() => renderTask.schedule();
widgetsChanged() => renderTask.schedule();
but this looks pretty broken. Maybe my Polymer element is architectured incorrectly (i.e. I have two attributes with widgets depending on lines)?
*Changed methods are definitely the right way to approach the problem. However, you're trying to force synchronicity in an async delivery system. Generally we encourage folks to observe property changes and react to them and not rely on methods being called in a specific order.
One thing you could use is an observe block. In that way, you could define a single callback for the two properties and react accordingly:
http://www.polymer-project.org/docs/polymer/polymer.html#observeblock
Polymer's data binding system does the least amount of work possible to rerender DOM. With the addition of Object.observe(), it's even faster. I'd have to see more about your element to understand what needs rendering but you might be creating a premature optimization.
I think there are three possible solutions:
See this: http://jsbin.com/nilim/3/edit
Use an observe block with one callback for multiple attributes (the callback will only be called once)
Create an additional attribute (i.e. isRender) that is set by the other two attributes (lines and widgets). Add a ChangeWatcher (i.e. isRenderChanged() in which you call your expensive render method)
Specify a flag (i.e. autoUpdate) that can be set to true or false. When autoUpdate = false you have to call the render method manually. If it is set to true then render() will be called automatically.
The disadvantage of solution 1 is that you can only have one behavior for all observed attributes. Sometimes you want to do different things when you set a specific attribute (i.e. size) before you call render. That's not possible with solution 1.
I don't think there is a better way. You may omit the 50ms delay (just Timer.run(() {...});) as the job gets scheduled behind the ongoing property changes anyway (my experience, not 100% sure though)

How to process elements added or removed from DOM by Dart Web UI template

In (latest) Dart Web UI, what's the best way to process an element when it's added or removed from the DOM by a template? Ideally I'd like to register a callback right in the template, but that's not a requirement.
Background: I need to register/unregister certain DOM elements from two JS libraries (one of which is a JQuery plugin). Since my template uses loops and conditionals (and data binding), elements can come and go at any time, and I can't just register them after the initial rendering.
It is possible to add callbacks to your component's class that trigger when it is either created, inserted into the DOM, or removed from the DOM.
Web UI Specification: Lifecycle Methods
class MyComponent extends WebComponent {
inserted() {
// Do stuff when inserted into DOM.
}
removed() {
// Do stuff when removed from DOM.
}
}

JSF: invoke action after value change

In the bad old days in my codebase we relied quite heavily on event requeuing, which I suspect worked due to implementation details in ICEfaces or MyFaces rather than standard-specified behavior. One thing we used to do frequently was this kind of thing:
<ice:inputText value="#{bb.frequency}" valueChangeListener="#{bb.valueChanged}"/>
The goal is to arrange for retune to be called after setFrequency whenever the frequency changes.
Then we had some fairly disgusting code in the backing bean which would requeue the event. It usually looked something like this:
class BB {
// this happens first, thanks to UPDATE_MODEL_VALUES
public void setFrequency(Frequency f) {
this.model.f = f;
}
public void valueChanged(ValueChangeEvent event) {
if (event.getOldValue().equals(event.getNewValue())
return; // nothing changed, so leave
if (FacesContext.getCurrentInstance().getPhaseId() != INVOKE_APPLICATION) {
OurMagicEventUtils.requeueEvent(event, INVOKE_APPLICATION);
}
else {
// do the post-setter work here (the setter happened recently during
// UPDATE_MODEL_VALUES so we're up-to-date by here
this.model.retune();
}
}
}
This isn't a good way to live. I haven't found a reliable way to requeue events for later phases and it clearly isn't the kind of thing people do. I see two solutions:
Move the retune intelligence to the BB#setFrequency method.
I can't get away with this in many cases because I'm directly addressing a lower-level model class and I don't want to disturb its behavior for other clients.
Create a custom component and move the logic into the setFoo method there.
I don't love this because there are a lot of issues with Mojarra and custom components when embedded in other containers. It also seems like overkill for what I need to do—I literally just need to call retune after setting some properties.
Create backing beans for everything. Delegate most methods directly to the inner thing, but catch setFoo and perform the retune there. This is very similar to what we used to do, and it means a lot of boilerplate, wrappers, and glue code, so I don't love it.
In my mind I imagine something like this:
<ice:inputText value="#{bb.frequency}" afterChange=#{bb.retune}"/>
but that obviously doesn't work, nor would attaching an <f:actionListener> since that requires a class name but has no association to whatever you're currently doing, and besides it can only be set on UICommands which UIInputs are not.
What's the elegant/correct way to solve this dilemma?
As you're using JSF2 already, just use <f:ajax>.
<ice:inputText value="#{bb.frequency}">
<f:ajax listener="#{bb.retune}"/>
</ice:inputText>
with
public void retune(AjaxBehaviorEvent event) { // Note: the argument is optional.
// ...
}
This will be invoked during invoke action phase when the HTML DOM change event has occured.

How to get the contextual Pojo when handling a JSF 2.0 Event

I am using a third party JSF 2.0 component (the Primefaces 3.0 FileUpload) that defines its own custom event. On the server side, the signature of the handler looks like this:
public void handleFileUpload(FileUploadEvent event)
Problem is, my form is built dynamically and may have dozens of separate FileUpload controls in it, and I need to know WHICH of the fileupload controls generated the event.
Actually, I don't need to know which, I just need the "var" that was in the ui:repeat that caused that particular FileUpload control to be generated. With normal controllers I could have easily just passed in the variable I need, but this 3rd party component happens to use an event handling mechanism rather than a controller, and being rather ignorant of how to work with JSF 2.0 events, I don't know how to get at the POJO, given only the event.
I see that event has a getComponent() method on it that tells me the UIComponent, but after poking around I don't see any easy way to get at the contextual variables, or even a way to generate my own EL expression to evaluate to get at the contextual variables.
So the question boils down to... given only an event, how can I get at the contextual variables in scope for the particular component that was clicked?
Figured it out... I needed to put this inside the 3rd party component
<f:attribute name="myObject" value="#{myObject}"/>
Then it is available in the attributes map of the component on the server side:
final MyOjbect myObject = (MyObject) event.getComponent().getAttributes().get("myObject");

Resources