JSF - how to properly use loops [c:forEach vs ui:repeat] - jsf-2

I'm trying to create a generic pageable table that will display any entity from database. I'm using annotations and reflection for that purpose. On the java side everything works fine, however I can't manage to create a view for my table. I tried two approaches, with c:forEach and ui:repeat:
Requirements for the view:
Fixed amount of records should be displayed on each page (eg. 10)
Components for browsing and editing table columns should be generated automatically based on viewed field type (field annotated in hibernate entity)
Additional column with actions should be added (removing, saving, filtering, adding entities)
Additional row for filtering and adding fresh entities should be added above table header
Each column has to be sortable
c:forEach approach problem:
When changing between pages that have different number of records, then proper records are displayed but number of rows is the same like on the previous page. If previous page had less records than next page will have less records than it should have and if previous page had more records than next page will have more records than it should have. However when I refresh the whole site on the given page everything renders properly.
On the first picture you can see, when I go to the last page unnecessary records are generated to match previous page size. On the second picture, page size is truncated to the last page size.
Table body code:
<tbody class="lot">
<c:forEach items="#{cc.attrs.wflBean.searchProvider.cells}" var="row" varStatus="rowStatus">
<tr>
<c:forEach items="#{row.cells}" var="cell" varStatus="cellStatus">
<td style="text-align: #{cell.alignment}; vertical-align: bottom;">
<h:commandLink action="#{cc.attrs.wflBean.editEntity}" value="Edit" styleClass="none"
rendered="#{cc.attrs.wflBean.isExternalEditSupported and cc.attrs.wflBean.canEdit}">
<f:setPropertyActionListener target="#{cc.attrs.wflBean.currentEntity}" value="#{cell.entity}"/>
</h:commandLink>
<!--TODO: cellIndex will not work properly for more then 10 columns/rows-->
<comp:pagableTableCell wflBean="#{cc.attrs.wflBean}" cell="#{cell}"
cellIndex="#{cellStatus.index}#{rowStatus.index}"
canEdit="#{not cc.attrs.wflBean.isExternalEditSupported and cc.attrs.wflBean.canEdit}"/>
</td>
</c:forEach>
...CODE FOR ADDING ADDITIONAL ACCTIONS...
</c:forEach>
</tbody>
pagableTableCell is a custom component that renders proper component for editing cell value it is done with c:choose/c:when/c:otherwise tags. When changing page, the cells are retrieved. Its done during RESTORE_VIEW and RENDER_RESPONSE phase. Cells for new page are recalculated and returned properly during RENDER_RESPONSE phase. Also, I read that c:forEach tag is evaluated during build view phase but I don't see what javax.faces.event.PhaseId is that? Page is set by another custom component and it is done properly right before RENDER_RESPONSE phase.
ui:repeat approach problem:
With this approach I replaced c:forEach with ui:repeat and c:choose/c:when/c:otherwise with ui:fragment but then, I can't evaluate EL expression within id attribute, to make it unique, for date component that you can see on images above. Unique id is required for date component to work properly. Is there any way to manually generate component id when using ui:repeat?
Overall I don't know how should I approach this problem. c:forEach seems to work better for me, at least table is usable. Could you advise me how to fix one of this problems?
Thanks,

Related

How to render a table of divs with JSF?

I need to draw a table with a column and a dynamic number of rows depending on a number of bills that I file with JSF without using an extension as PrimeFaces.
I've been looking for how to use the component h:panelGrid with h:panelGroup inside for bills without finding a solution.
welcome your suggestions
If you want to create a table of divs, one way would be to use iterating tag :
View :
<ui:repeat value="#{bean.rows}" var="row">
<div>
#{row.data}
</div>
</ui:repeat>
Bean :
public List<YourObject> getRows()
{
return this.rows;
}
This is only a draft, but it show you the way. If you add more informations how your data is constructed, It will be easier to give a more detailed answer.

Wrong JSF 2 ID Chaining

Situation
JavaServer Faces Version: 2.1.6
I got a parent composite component with two nested cc. One of them contains a HtmlPanelGroup, which has a component binding. I use that binding to programmatically add HtmlCommandLink-Objects to the HtmlPanelGroup.
Let's call some IDs:
PARENT for the parent cc
CHILD_FIRST for the first child cc, nested in PARENT
CHILD_SECOND for the second child cc, nested in PARENT
GROUP for the PanelGroup, nested in CHILD_SECOND
LINK_1 for the first HtmlCommandLink-Object, progammatically added to GROUP
Expectation
I expect the following ID Chaining (with default javax.faces.SEPARATOR_CHAR):
PARENT (Composite Component, declarative)
PARENT:CHILD_FIRST (Composite Component, declarative)
PARENT:CHILD_SECOND (Component Component, declarative)
PARENT:CHILD_SECOND:GROUP (HtmlPanelGroup, declarative)
PARENT:CHILD_SECOND:GROUP:LINK_1 (HtmlCommandLink, programmatically)
Problem
The ID of the HtmlCommandLink-Object is wrong at the first page visit. Instead of "PARENT:CHILD_SECOND:GROUP:LINK_1" the ID is only "CHILD_SECOND:GROUP:LINK_1". After I refresh the page the ID is correctly "PARENT:CHILD_SECOND:GROUP:LINK_1". In fact every component in the second composite component ("CHILD_SECOND") is missing the first part of the ID ("PARENT"). After refresh all IDs are correctly.
Solution
I might automatically refresh the page after the first visit. But I don't want to.
the id after interpreted by the browser doesn't like what you thought. as far as i know it doesn't exceed 3 levels. For example:
<h:form id="form">
<h:panel id="panel">
<h:panel id="panel1">
<h:label id="lab"/>
</h:panel>
<h:panel id="panel2">
</h:panel>
</h:panel>
</h:form>
then the label's id will not be form:panel:panel1:lab but form:panel:lab. if the widget that you want to get the id is more deeper, then i can't tell but it won't exceed 3 levels. i can tell you how to find the id.
you can just write the page, and then view it on the chrome or firefox where you can see the source code after interpreted. so you can get the id you want.
good luck!

MVC3 ModelBinding to a collection posted back with index gaps

I have a collection of objects on my Model that I'm rendering in a View by using EditFor function, and I have an EditorTemplate which is responsible for actually rendering each object.
#Html.EditorFor(model => model.MyObjects)
This has worked well for a while now, and when you check the html, my text boxes are prefixed with the model property, followed by the index of the collection they came from.
<input class="text-box single-line" id="MyObjects_2__SomeProperty"
name="MyObjects[2].SomeProperty" type="Text" value="" />
However I've recently started using the ShowForEdit and ShowForDisplay properties in the model metadata for the collection, and in the first line of my editor template if the ShowForEdit is not true, I just skip it.
#if (!ViewData.ModelMetadata.ShowForEdit)
{
return;
}
But because these are all indexed in the html, when I try to save this collection back to the viewmodel via a postback, it fails because of a reliance on the indexing numbers. Every item in the collection after the missing index is missing from my view model when I check it's value.
In this case it's actually my first item in the collection that I'm skipping since I don't want it to be visible on the edit view, but because of this when I postback the first index in the html is 1 (instead of 0 like it normally would be), but this is a problem when you try to save the changes. This is also a problem when altering the DOM using javascript.
Has anyone else encountered a problem with the default model binder's ability to read data posted back when one or more indexes in the html represented collection are not present?
Are there model binders that handle this problem?
Ran into this issue recently and solved it by converting the List to a Dictionary<string, model> with GUIDs as the key.
#foreach (var index in Model.EmailAddresses.Keys)
{
<label asp-for="#Model.EmailAddresses[index].Email">Email</label>
<input asp-for="#Model.EmailAddresses[index].Email" type="text" />
}
This avoided having to include hidden inputs that map to the index value.
There are some very good blog posts that allow you to modelbind to a list without the need to provide zero based contiguous index. plz have a look at
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
http://zahidadeel.blogspot.com/2011/05/master-detail-form-in-aspnet-mvc-3-ii.html
Furthermore, if you are interested in MVVM pattern and knockout js you can check this great work by steve sanderson
For more reading put "editing varibale length list mvc style" in google and it will give u a dozen useful links

Maintain state of a dynamic list of checkboxes in ASP.NET MVC

I have a class called "PropertyFeature" which simply contains PropertyFeatureID and Description. It's a proper model created through LINQ to SQL mapped to an SQL Server database table. An example instance/row would be:
PropertyFeatureID: 2
Description: "Swimming Pool"
The number of rows (PropertyFeatures) can of course grow and shrink, and so I want to dynamically render a list of checkboxes so that the user can select any of them.
I can dynamically render the Checkboxes easily enough, with something like:
<%foreach (var Feature in (ViewData["Features"] as IEnumerable<MySolution.Models.PropertyFeature>)) { %>
<%=Html.CheckBox("Features", new { #id = Feature.PropertyFeatureID, #value = Feature.PropertyFeatureID })%><label for="Feature<%=Feature.PropertyFeatureID%>"><%=Feature.Description%></label>
<%}%>
I specify the ID for each checkbox and render the matching label so that the user can intuitively click the label and toggle the checkbox - that works great.
I set the CheckBox's "name" to "Features" so all the checkboxes render with the same name, and the MVC Model Binder piles them into a single collection called "Features" when the form is posted. This works nicely.
Once the form is submitted, I use the checked values and store them, so I need the actual integer values so I know which PropertyFeature is selected, not just a pile of Booleans and field names. So ideally, I want it as an array or a collection that's easy to work with.
I am able to retrieve the selected values from within my Controller method when the button is clicked because I have specified the parameter as int[] Features.
But the problem is that it doesn't maintain state. That is, when I click the submit button and the page reloads (with the form again displayed) I want all of the dynamic checkboxes to retain their checked status (or not). All of the other fields that I've created with Html.DropDownList and Html.TextBox all maintain their states successfully no problems at all on the same page in the same form.
I have spent hours reading all of the other threads and articles on similar issues and there is a lot of talk about using ICollection and IDictionary to bundle things up and include a Boolean value for each item so that it can maintain the checkbox state. But I don't 100% grasp how to use that in the context of my own personal example. I would like to keep the solution really simple and not have to code up pages of new classes just to maintain my checkbox state.
What is the cleanest and proper way to do this?
I got it working after much playing around with the various different approaches.
In the view:
<%string[] PostFeatures = Request.Form.GetValues("Features");%>
<% foreach (var Feature in (ViewData["AllPropertyFeatures"] as
IEnumerable<MySolution.Models.PropertyFeature>))
{ %>
<input type="checkbox" name="Features"
id="Feature<%=Feature.PropertyFeatureID.ToString()%>"
value="<%=Feature.PropertyFeatureID%>"
<%if(PostFeatures!=null)
{
if(PostFeatures.Contains(Feature.PropertyFeatureID.ToString()))
{
Response.Write("checked=\"checked\"");
}
}
%> />
<label for="Feature<%=Feature.PropertyFeatureID%>">
<%=Feature.Description%></label> <%
} %>
In the receiving controller method:
public ActionResult SearchResults(int[] Features)
This method has a number of advantages:
Allows labels to be clicked to toggle the corresponding checkboxes (usability).
Allows the Controller method to receive a super tidy array of ints, which ONLY contains the ints that have been selected - and not a whole other pile of items which were unselected or containing false/null/blank/0 etc.
Retains the checkbox's checked state when the page reloads containing the form, i.e. the user's selection is retained.
No random/stray type=hidden input fields created from the default ASP.Net MVC Html.CheckBox helper - I know it does those for a good reason, but in this instance, I don't require them as I only want to know about which IDs have been selected and for those to be in a single, tidy int[].
No masses of additional server side bloated classes, helpers and other happy mess required to achieve such a simple thing.
I would recommend this approach for anyone wanting the cleanest / bloat-free solution for a dynamic checkbox list where you need the IDs and you just want to get down to business!
The problem is that when you are rendering your list of checkboxes, you aren't setting any of them as selected. You will need to set your int[] Features in ViewData, and then in your foreach loop, check to see if the ID of that Feature is in the array in ViewData.
something like:
<%=Html.CheckBox("Features",
((int[])ViewData["SelectedFeatures"]).Contains(Feature.PropertyFeatureID),
new { #id = Feature.PropertyFeatureID, #value = Feature.PropertyFeatureID })%
although I didn't test it, so it might not be 100%.

Replacing a <tr> using ASP.MVC Ajax

I have the following ajax form tag:
When the form is submitted, the controller returns a partial with a full row to be inserted into the table (the same partial is also used to render the table in the first place).
The idea is that after the user edits an item, the item's row in the table will be replaced with the updated version from the partial. When I point UpdateTargetId to a <div> element it seems to work fine, but when I point it to a <tr> element, it doesn't.
Any help would be greatly appreciated.
I'm assuming that your partial actually renders the entire row. In that case, the default replacement semantics won't work for the <tr> element since you'll be inserting a new row into the existing row and get something like:
<tr><tr>...new contents</tr></tr>
You might want to look at changing the InsertionMode (I forget the other potential options) or having your partial generate just the row contents, i.e., the <td> elements instead of the row itself.
I ended up changing my approach. I changed the controller to return the values of the object in jason and had the view's js function simply update the content of the table row with the new values. Seems to work fine now.
Rich Strahl has an excellent post on how to do Client Templating with jQuery.

Resources