JSF tag handler limitation? - jsf-2.2

I think there is a limitation to the usage of a tag handler: a tag handler cannot be safely added to a c:forEach tag.
I can understand that this type of construct may look strange, but I faced a situation where the number of custom changes made to a component had to be dynamic, as in the follow example:
<my:component ...>
<c:forEach items=#{bean.items} var="item">
<my:tag ... />
</c:forEach>
</my:component>
This construct does not work in a postback if the number of items changes.
During the restore view phase, the number of items is equal to the number of items that was rendered in the previous response. In this phase, the parent component of the tag handler is new.
During the render response phase, the number of items may have changed if, for instance, the bean model has been updated since the previous render response phase. During this phase, when in a postback, the parent component of the tag handler is not new.
As we don't want to apply the same changes twice to the parent component (restore view, and render response), we first check whether the parent component of the tag handler is new, and we apply the tag if and only if it is new.
But in that case, during a postback, the changes that are applied to the parent component are not those specified by the bean after is was updated, because in the render response phase the parent component is not new anymore.
I'm not sure there is a simple solution to this use case, and maybe should I use a component instead of a tag in that case?
Remark:
In the restore view phase, the tag handler applies the changes to its parent component.
Then the state of the parent component is restored.
If the changes applied to the parent component were saved in component's state holder, then these changes are restored after the tag handler is applied.
Therefore, during the restore view phase, these changes are first applied by the tag handler and then replaced by those that were saved. Looks redundant, no?
Possible workaround?
I've tried a workaround that seems to work in the case where the changes applied to the parent component only concern the way it is rendered:
Apply the tag to the parent component only in the render response phase, no matter if the parent component is new or not.
Do not save the changes applied by the tag into the parent's component state holder.
Of course this approach does not work if the applied changes had to be use between the restore view phase and the render response phase.

Related

Is it possible to stop the propagation of a taghelpers in one of the taghelpers linked to that tag?

I would like to stop the propagation of any taghelpers linked to a tag. I have a attribute based taghelper that I use to check whether the user can see or use a tag, and based on that I would like to stop any further processing to happen.
output.SuppressOutput();
does not work in this regard.
Some more detail.
I have a grid that has rows and actions per row ( actually there are other types of actions, ie on the grid etc) the rendering of the actions happens using a grid renderer.
The actions are added to a context that is provided to the grid via the items property.
I have another taghelper that can be placed on any tag element which determines whether the user has rights to see the element or whether it is disabled. This taghelper is first in the hierarchy.
Now since the actual action taghelper doesn't render itself, the grid renders it, I can't suppress the output of the action, as it continues to the next taghelper in the hierarchy and adds it to the context.
I would like it stop executing the taghelper hierarchy and not add it to the context, without being able to stop the execution I have to now pass whether the action should be displayed or disabled along in the context and update all my various renderers code to handle this. Stopping the exectution is much simpler and touches a lot less code.

Primefaces nested p:dialog DOM node duplicates

I have a list element detail form opening inside a p:dialog. This p:dialog in turn shows another list. From this list, I can open any of its element's details inside a nested p:dialog.
The problem is: every time I open a dialog, a new set of ids for the elements in the nested dialog is generated, with the same value.
What I end up with in my DOM when I try to select a particular id from the nested dialog, such as $('#manageIssue\\:newEventComment');, is an array of elements with the same id.
I have determined there is one copy per each time I open a dialog, plus another one which is there from the beginning.
The nested dialog DOM nodes are not being destroyed when the nested dialog is closed, and a fresh set with overlapping ids is being generated every time a dialog is opened.
This question is related to primefaces update attribute not working on modal dialog opened from modal dialog.
(I solved the original problem by removing the prependId attribute from the form, but this one remained.)
Because this problem is a little difficult to reproduce, I have built a MCVE. All the stuff (backing beans, views, pom.xml, etc.) adds up to ~500 lines of code, so I have shared it on a github repo: https://github.com/elcodedocle/testt
The question here boils down to:
How can I make this MCVE work (i.e. add events with comments to an issue from a list of issues of a commission from a list of commissions), without this line:
https://github.com/elcodedocle/testt/blob/fbfeb7fca474c66c202c92e469ca185c6bf569c2/src/main/webapp/views/widgets/issue_detail_edit.xhtml#L21
?
This problem is caused by nested <p:dialog>.
<p:dialog id="commissionDetail">
...
<p:dialog id="issueDetail">
...
</p:dialog>
</p:dialog>
This is not allowed. The technical reason is, the HTML DOM element representing the dialog is by JavaScript relocated to the very end of <body> in order to ensure best cross browser compatibility as to calculating the z-index and offset. Then, when you ajax-update the dialog, the existing dialog couldn't be found in its original parent element in the DOM, so simply a new one is added to DOM (which then get relocated to end of body again, etc).
You really need to restructure your templates so you ultimately end up like
<p:dialog id="commissionDetail">
...
</p:dialog>
<p:dialog id="issueDetail">
...
</p:dialog>

JSF, AJAX and the rendered attribute

If you do JSF AJAX calls and change the component tree while rerendering (or between ajax calls), you'll get exceptions from Mojarra. As I understand it, it's difficult to recreate the component tree partially when the new tree is different as the one stored in the ViewState (or the actual JSF class). That's "ok". I'm thinking about using the rendered attribute and not rendering the component.
My question: How does the rendered attribute work? Does the component get restored and is the component tree, that JSF creates during restore phase, safe? We have a very dynamic XHTML page and not rendering object's instead of disabling them with css classes would really up the speed of the page.
I tried it and it works as expected. So JSF only assumes that the component is there even if it doesn't render anything.
Please consider that the view state does get restored and it's still a performance hit (but a smaller one as nothing get's sent over the wire and the output string/html doesn't need to be rendered).

JSTL tag management on RichFaces 3 re-rendering

I read various examples on the web where an ajax call sent through a4j tags of ReachFaces 3 re-renders a jstl tag. In this example:
http://relation.to/Bloggers/UsingDynamicallyCreatedRichFacesTabPanelForSearchResults
an ajax call re-renders a c:foreach inside a rich:tabPanel (look at the code after "Add the next code just after the panel code:").
My question is: since the c:forEach tag is evaluated during view creation (like any other jstl tag), the portion of the view pointed by the reRender attribute is reconstructed from scratch on every ajax request?
Thanks a lot.
Nico
Looking at JSF lifecycle docs, more specifically the Restore View Phase and Render Response Phase parts, you can find what are you looking for:
Restore View Phase
If the request for the page is an initial request, the JavaServer Faces implementation creates an empty view during this phase and the life cycle advances to the render response phase. The empty view will be populated when the page is processed during a postback.
If the request for the page is a postback, a view corresponding to this page already exists. During this phase, the JavaServer Faces implementation restores the view by using the state information saved on the client or the server.
Render Response Phase
During this phase, the JavaServer Faces implementation delegates authority for rendering the page to the JSP container if the application is using JSP pages. If this is an initial request, the components represented on the page will be added to the component tree as the JSP container executes the page. If this is not an initial request, the components are already added to the tree so they needn't be added again. In either case, the components will render themselves as the JSP container traverses the tags in the page.
In short words, when you perform an Ajax request, you already have the view there. JSF will just search the components you want to rerender through it, just after their model have been updated, and update the view with their new values. In case of c:forEach, it's not a component, but a tag handler, so it's evaluated before that.
Related to the link you posted, have a look at this page about the difference between components and tag handlers. They just have different lifecycles, so take care when mixing them.

Moving an ItemView from one CollectionView in another without deleting it, best practice?

I'm building a dashboard builder and view interface with Marionette. I have some views from legacy code that are pretty heavyweight(large reports) and the html is thus pre-constructed on the server.
I have a Marionette CollectionView for each row in the dashboard which contains an ItemView for each widget that was dragged onto the row during dashboard building.
When the user moves a widget from one row to another I want to avoid deleting the view and having to reconstruct it (because it would be a lot of unnecessary dom manipulation) but instead want to just detach the element from one(row) CollectionView and add it to another. What's the best practice for accomplishing this with CollectionViews in Marionette?
It seems by default moving a item across CollectionViews would destroy the view/model from one and re-instantiate/re-render it in the other.
The concern I have is that the tablereport in the DOM that would be moving from one collection to another is not original Marionette/Backbone template generated View, it would be just a predefined DOM element we set as the view's el.
The tablereport DOM element has lots of children elements with events associated with it via legacy code not the Backbone view events array nor via Backbone's listenTo calls. So destroying the DOM tablereport element is what we need to avoid to preserve those events, we just want to relocate it in the DOM.
Whats the best way to handle this functionality efficiently in Marionette.
A couple options spring to mind, but the fact that your event-binding code is not easily callable means you'll probably want to use the jQuery detach() method to keep all your events bound when you remove an element.
One option is to cache your view elements as you build them:
- write a view factory which you delegate to in the buildItemView method of your CompositeView.
- have your factory cache the elements for the views it creates against the model.cid of the model that is passed in as the first parameter to the buildItemView method.
- when your factory method is called, retrieve the element from the cache if it exists, call detach() on it, and set it as the ItemView's element.
- override the render method on the view to stop it from rebuilding the html
- move the model from one CollectionView's collection and put it into the other CollectionView's collection, and Marionette will then build your view as described above and insert the element (with events still bound) into the DOM.
Instead of doing the above, you could, assuming you know which ItemView has been dropped:
- remove the ItemView from the first CollectionView's children container (this is a Backbone.Babysitter instance, I believe, and there is documentation for it)
- detach the ItemView's element
- insert the ItemView's element into its new place in the DOM
- insert the ItemView into the second CollectionView's children container
- remove the model from the first CollectionView's collection
- add the model into the second CollectionView's collection silently, which prevents the CollectionView from triggering its normal behaviour of building a new view, rendering it and inserting it.
The first way is probably more elegant as you are still letting Marionette do its thing, but just altering the building, rendering and inserting of the ItemViews. The second way is less complex, but means you have to manually keep everything in sync yourself - essentially doing what Marionette would normally do behind the scenes and stopping it from 'interfering', so to speak.

Resources