Problems with JSF datatable and Bootsfaces modal windows - jsf

I am using JSF for my webapp (combined with EJB) and Bootsfaces as JavaScript addition to show modal windows. I am having a list of items that are being shown through a dataTable. And by clicking on a command button inside my list I am opening a modal window, asking the user "wether he is sure he wants to delete". That modal confirm dialog has YES and NO. I can execute the yes execute button inside the modal window. But I cant do #{controller.delete(item)} because the item is only server side available while building the list table. Somehow I have to send the actual selected item to the modal window to set it somehow in that controller call...???
Anyone got an idea?
<!-- looping the jsf list //-->
<h:dataTable value="#{controller.itemlist}" var="item"...
...
<!-- showing modal window //-->
<h:commandButton value="delete" action="" onClick="return false;" p:toggle-data.... />
</h:dataTable>
</h:form>
...
<!-- modal window in panel, with item not set //-->
...
<h:commandButton action="#{controller.delete(item)}" />
</h:form>
</b:panel>
...

You could use ajax to populate your modal's content :
$.ajax({
url : "yourPage.jsf",
type : "POST",
data : {
id: "your object's id"
},
dataType : "html"
}).done(function(html) {
$("#yourModal").html(html);
});
This ajax should be called with onclick event on your <h:commandButton>
And of course, you'd need to create a JSF view for your modal content. This view will contain your text and your two buttons "Yes" and "No". "No" should just dismiss your modal, while "Yes" should call your bean action : <h:commandButton action="#{controller.delete(item)}" />.
Don't forget to populate your controller item with the id parameter to bind data to your view.
EDIT :
I'll try to show you an example.
Main View (view.xhtml) :
<h:form>
<h:dataTable value="#{controller.itemlist}" var="item">
<h:column>
...
</h:column>
<h:column>
<h:commandButton value="delete" onclick="return populateModal(#{item.id}); " />
</h:column>
</h:dataTable>
</h:form>
Script to populate your modal (view.xhtml) :
<script type="text/javascript">
function populateModal(id) {
$.ajax({
url: "view2.jsf",
type: "POST",
data: { id: id },
dataType: "html"
}).done(function(html) {
$("#modal").html(html);
$("#modal").show();
});
return false;
}
</script>
Then you need a view for your modal's content (view2.xhtml) :
<h:form>
Delete #{testBean2.item.name} ?
<h:commandButton value="YES" action="#{testBean2.delete(testBean2.item)}" />
<h:commandButton value="NO" onclick="return closeModal();" />
</h:form>
And a controller for this view (testBean2.java) :
private Item item;
#PostConstruct
public void init() {
// GET id parameter from HTTPRequest
// Populate Local Item
}
public void delete(Item item) {
// Delete your item
}

Related

How to make selected h:selectOneRadio of h:dataTable remain selected on postback?

In normal circumstances like this:
<h:form>
<h:selectOneRadio value="#{bean.gender}">
<f:selectItem itemValue="Male" itemLabel="Male" />
<f:selectItem itemValue="Female" itemLabel="Female" />
<f:selectItem itemValue="Other" itemLabel="Other" />
</h:selectOneRadio>
<h:commandButton value="Submit" action="#{bean.action}" />
</h:form>
Selecting one radio button disselects the other & the radio button will be remain selected on the postback. (when the same view is rendered)
However, when we're dealing with an iterating component like <h:dataTable>, the selection is lost.
Consider the snippet:
<h:form id="hashMapFormId">
<b>HASH MAP:</b>
<h:dataTable value="#{playersBean.dataHashMap.entrySet()}" var="t" border="1">
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectOneRadio id="radiosId" onclick="deselectRadios(this.id);"
value="#{playersBean.select}">
<f:selectItem itemValue="null"/>
</h:selectOneRadio>
</h:column>
</h:dataTable>
<h:commandButton value="Show Hash Map Selection"
action="#{playersBean.showSelectedPlayer()}" />
</h:form>
With disselecting the other radio buttons when one radio button is selected being implemented by simple JavaScript-
function deselectRadios(id) {
var f = document.getElementById("hashMapFormId");
for (var i = 0; i < f.length; i++)
{
var e = f.elements[i];
var eid = e.id;
if (eid.indexOf("radiosId") !== -1) {
if (eid.indexOf(id) === -1) {
e.checked = false;
} else {
e.checked = true;
}
}
}
}
Fire the GET request:
Select a radio button:
Now press the submit button, response:
You see that the radio button gets dis selected on postback. How to solve this shortcoming?
I know it very well that this is due to this component attribute itemValue being null:
<f:selectItem itemValue="null"/>
This trick is a leftover from JSF 1.x / 2.0/2.1 when it wasn't possible to use a <h:selectOneRadio> for single row selection in a <h:dataTable>. This trick originated in my 10 year old blog article Using Datatables - Select row by radio button.
The root problem is, HTML radio buttons are grouped based on their name attribute, so the webbrowser knows which others to unselect when one is selected. But JSF generates by design a different one for each <h:dataTable> item, with the row index inlined and therefore they can't be grouped and hence the JavaScript based workaround.
Since JSF 2.2, with the new passthrough elements and attributes feature, it's however possible to force the name attribute to the value of your choice and capture the selected item via a helper <h:inputHidden>. This is fleshed out in another blog article of me, from previous year: Custom layout with h:selectOneRadio in JSF 2.2. The article uses <ui:repeat> as an example, this can be rewritten to <h:dataTable> as below.
<h:form>
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
value="#{item.id}" a:checked="#{item.id eq bean.selectedItemId ? 'checked' : null}" />
</h:column>
<h:column>#{item.id}</h:column>
<h:column>#{item.name}</h:column>
</h:dataTable>
<h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItemId}"
rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>
#Named
#ViewScoped
public class Bean implements Serializable {
private List<Item> items;
private Long selectedItemId;
// ...
public void submit() {
System.out.println("Selected item ID: " + selectedItemId);
}
// ...
}
And yes, the selected radio button remains selected on postback this way. You can also pass whole entities, this only requires a converter on the <h:inputHidden>.

h:selectBooleanCheckbox always pass false to validator

I have h:selectBooleanCheckbox but it passes 'false' to the validator. always.
<h:panelGroup id="tncPanel">
<label>
<h:selectBooleanCheckbox id="tncSelection" value="#{businessBean.tncSelection}">
<f:validator validatorId="checkboxValidator"/>
<f:attribute name="label" value="Please agree terms and conditions."/>
</h:selectBooleanCheckbox>
I have read and agreed to all the
<a href="#" data-toggle="modal" data-target="#modal-term-and-conditions">
Terms and Conditions.
</a>
</label>
</h:panelGroup>
<h:panelGroup id="buttonPanel">
<h:commandLink id="nextButton" action="#{businessBean.goNext}">
Submit
</h:commandLink>
</h:panelGroup>
Why I have panelGroup here is, based on logic in top of page, I have a logic to display/not the button and checkbox
This is my Validator.
#FacesValidator("checkboxValidator")
public class CheckboxValidator implements Validator {
private static final String DEFAULT_LABEL = "Please confirm the address.";
#Override
public void validate(FacesContext context, UIComponent component, Object value){
String checkBoxValue = String.valueOf(((HtmlSelectBooleanCheckbox) component).getSubmittedValue());
System.out.println("checkBoxValue " + checkBoxValue);
System.out.println("value2: " + value);
String label = String.valueOf(component.getAttributes().get("label"));
if ("null".equals(label)) {
label = DEFAULT_LABEL;
}
if("false".equalsIgnoreCase(checkBoxValue)){
FacesMessage msg = new FacesMessage(null, label);
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
Validator sysout always prints checkBoxValue false
UPDATE
After comment on Balusc, I add another sysout to print directly the parameter value. But still it's printing as value2: false.
There is a bug related to this situation , When you render a container that holds a from from another form you the rendered form loses its view state ,Thus form is submitted to no destination solution is to render the form container and the form its self from the other form
For example that won't work :
<h:panelGroup id="panel">
<h:form>
<h:commandButton value="Submit" rendered="#{not testAction.flag}">
<f:ajax render=":panel" listener="#{testAction.submit()}"/>
</h:commandButton>
</h:form>
<h:panelGroup id="panel">
<h:form id="secondForm" rendered="#{testAction.flag}">
<h:selectBooleanCheckbox id="checkBox" value="#{testAction.boolVal}">
<f:validator validatorId="checkboxValidator"/>
</h:selectBooleanCheckbox>
<h:commandButton value="Go" action="#{testAction.go()}"/>
</h:form>
</h:panelGroup>
Second form submit will be like this :
secondForm won't have its view state the form is submitted but with no destination on the server cuz the view object is not fetched on the server that requires the javax.faces.ViewState value to be fetched by what you have to do here is to render form as well in the f:ajax:
<f:ajax render=":panel :secondForm" listener="#{testAction.submit()}"/>
That inform server to backup the second form rendered with the javax.faces.ViewState
Refer to that post : Rendering other form by ajax causes its view state to be lost, how do I add this back? for more info
Hope that was the problem :)

How to refresh jsf page after pagination

I have created selectALL checkbox functionality in jsf + icefaces. and i have implemented functionality like when i check selectAll checkbox it is selecting all the checkboxes across all the pages. but when i do pagination and if i do checked/unchecked select all checkbox then its works for only that page not acorss all the pages. i mean the state of row checkboxes is not consistent. i tried phaseListener for solving this but no luck.. can anyone suggest me the solution..
my code : jsf
<ace:column id="checkBox">
<f:facet name="header">
<h:outputLabel>
<ice:selectBooleanCheckbox id="selectAllID"
value="#{shipBean.checkAll}"
valueChangeListener="#{shipBean.getCheck}"
partialSubmit="true" />
</h:outputLabel>
</f:facet>
<ice:selectBooleanCheckbox id="selectOne"
value="#{freight.checkBox}">
java:
if (shipTrackingData != null) {
if (shipTrackingData.getCheckAll()) {
for (ShipTrackBean check : shipTrackingData
.getShipTrackList()) {
check.setCheckBox(true);
}
} else {
for (ShipTrackBean check : shipTrackingData
.getShipTrackList()) {
check.setCheckBox(false);
}
}
}
Thanks...
I have tested this code for a JSF application, I have three pages:
index_1.xhtml
index_2.xhtml
index_3.xhtml
#ManagedBean
#SessionScoped
public class pross {
private boolean challval=false;
private boolean chval1=false;
private boolean chval2=false;
private boolean chval3=false;
.. getters and setters....
}
In each page I created two checkboxes one of them is to be selected across all pages if selected in any of them, but that checkbox is related to the same value (challval) for all pages.
there is also another checkbox for the page itself, one for each (chval1,chval2,chval3).
To test this I wrote this xhtml code for every page but you need to change the values according to each page, here is the code for index_1.xhtml, you need to change "go to" links to be able to access other pages:
<h:body>
<h:form>
Hello, Test checkboxes:
<br />
Select this page: <h:selectBooleanCheckbox value="#{pross.chval1}" >
<f:ajax render="outthis outall"/>
</h:selectBooleanCheckbox>
This page select: <h:outputText id="outthis" value="#{pross.chval1}"/>
<br/>
Select all pages: <h:selectBooleanCheckbox value="#{pross.challval}">
<f:ajax render="outthis outall"/>
</h:selectBooleanCheckbox>
all pages select: <h:outputText id="outall" value="#{pross.challval}"/>
<br/>
<h:link outcome="index_2" value="Go to page 2" />
<br/>
<h:link outcome="index_3" value="Go to page 3" />
</h:form>
</h:body>
I see no problem here, and when I check "select all" in one page, it is still checked in the others, I think the idea is to connect the "select all" checkbox with same bean value for all pages.
Hope this is useful, thanks.

How to confirm an action using a modal window in JSF?

I have a JSF MyFaces dataTable that contains a list of elements and a column with a delete button. All I want to do is to popup a dialog when clicking on the delete button that would allow the user to confirm or cancel the operation.
I already have the dialog (reduced for simplicity and using <a> because of the lack of HTML5 support):
<div id="myModal">
<h:form>
<a data-dismiss="modal">Close</a>
<h:commandLink action="#{somethingMagicInHere?}">Confirm</h:commandLink>
</h:form>
</div>
In the dataTable I have something like this (also simplified):
<h:dataTable id="myDataTable" value="#{bean.elementList}" var="element">
<h:column>
<f:facet name="header">Actions</f:facet>
<a class="call-modal">Delete</a>
</h:column>
</h:dataTable>
Finally my ManagedBean looks like this:
#ManagedBean(name = "bean")
#RequestScoped
public class ElementClassBean {
...
public String actionToPerform(ElementClass e) {
MyBusinessLogicModel.getInstance().deleteElement(e);
}
}
So, in short, jQuery executes when loading the page and takes all elements with class call-modal and sets an onclick to them so that they display the component with id myModal, which is of course the modal window. I inherited this working this way and prefer not change it but any solution or ideas will help.
I can use a commandLink directly in the dataTable that would access actionToPerform(element) from the view but that, of course, won't fire the modal. So the main issue I see, given this structure, is how can I send the element being iterated in the dataTable to the modal once the Delete button is clicked? (I don't mind if the solution uses Ajax).
Any input will be helpful. Thanks.
Ok, this is the ugly but working solution that doesn't require me to refactor all the views and managed beans. In short: I added a hidden input field that would store the id of the element to delete in the modal form. In the datatable all I do is setting the value of the hidden input field once the button is clicked and fire the modal. The modal is then filled with the just updated value.
My simplified modal:
<div id="myModal">
<h:form id="myForm">
<h:inputHidden value="#{bean.elementIdInModal}" id="elementIdInModal"/>
<a data-dismiss="modal">Close</a>
<h:commandLink action="#{bean[actionToPerform]}">Confirm</h:commandLink>
</h:form>
</div>
My simplified dataTable:
<h:dataTable id="myDataTable" value="#{bean.elementList}" var="element">
<h:column>
<f:facet name="header">Actions</f:facet>
<h:link styleClass="call-modal"
onclick="$('#myForm\\:elementIdInModal').val(#{element.id})">
Delete
</h:link>
</h:column>
</h:dataTable>
My simplified ManagedBean:
#ManagedBean(name = "bean")
#RequestScoped
public class ElementClassBean {
private long elementIdInModal; // Ommiting getters and setters
public void actionToPerform() {
MyBusinessLogicModel.getInstance().deleteElement(elementIdInModal);
}
}

Inserting multiple datas in 1 entity in a page

i have a CRUD generated create form:
<div class="create-form">
<h:form>
<h:inputText id="name" value="#{pointController.selected.name}" title="#{bundle.CreatePointTitle_name}" required="true" />
<h:inputText id="term" value="#{pointController.selected.term}" title="#{bundle.CreatePointTitle_term}" required="true" />
<p:commandButton styleClass="btn" action="#{pointController.create}" value="#{bundle.CreatePointSaveLink}" />
</h:form>
</div>
<button>add new form</button>
i have a button that if clicked it will create another form same as above using javascript. (2 inputText, name and term)
my goal is, with 2 or more forms, depending how many forms the user wants, with 1 commandButton that is clicked it will insert everything in the database.
example:
first form: name = first form test, term = first form testterm
2nd form: name = 2nd form test, term= 2nd form testterm
after clicking the command button
2 rows will be inserted in the same table in the database.
but i'm not sure what would be the structure for page for this.
You can't send data from many forms in a single request using JSF components, you should serialize all the data and send it manually. It would be better to have a List<Item> and every time you click in the button it will create a new item on the list and update an UIContainer that will display the items of the list.
This would be a start example of the above:
#ManagedBean
#ViewScoped
public class ItemBean {
private List<Item> lstItem;
public ItemBean() {
lstItem = new ArrayList<Item>();
addItem();
}
//getters and setter...
public void addItem() {
lstItem.add(new Item());
}
public void saveData() {
//you can inject the service as an EJB or however you think would be better...
ItemService itemService = new ItemService();
itemService.save(lstItem);
}
}
JSF code (<h:body> content only):
<h:form id="frmItems">
<h:panelGrid id="pnlItems">
<ui:repeat value="#{itemBean.lstItem}" var="item">
Enter item name
<h:inputText value="#{item.name}" />
<br />
Enter item description
<h:inputText value="#{item.description}" />
<br />
<br />
</ui:repeat>
</h:panelGrid>
<p:commandButton value="Add new item" action="#{itemBean.addItem}"
update="pnlItems" />
<p:commandButton value="Save data" action="#{itemBean.saveData}" />
</h:form>

Resources